
001package org.hl7.fhir.r4.utils; 002 003import java.util.ArrayList; 004import java.util.HashMap; 005import java.util.List; 006import java.util.Map; 007 008import org.apache.commons.lang3.NotImplementedException; 009import org.hl7.fhir.exceptions.DefinitionException; 010import org.hl7.fhir.exceptions.FHIRException; 011import org.hl7.fhir.exceptions.FHIRFormatError; 012import org.hl7.fhir.r4.conformance.ProfileUtilities; 013import org.hl7.fhir.r4.context.IWorkerContext; 014import org.hl7.fhir.r4.model.Base; 015import org.hl7.fhir.r4.model.BooleanType; 016import org.hl7.fhir.r4.model.CanonicalType; 017import org.hl7.fhir.r4.model.Coding; 018import org.hl7.fhir.r4.model.DateTimeType; 019import org.hl7.fhir.r4.model.DateType; 020import org.hl7.fhir.r4.model.DecimalType; 021import org.hl7.fhir.r4.model.Element; 022import org.hl7.fhir.r4.model.ElementDefinition; 023import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent; 024import org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent; 025import org.hl7.fhir.r4.model.Enumeration; 026import org.hl7.fhir.r4.model.Enumerations.BindingStrength; 027import org.hl7.fhir.r4.model.Enumerations.PublicationStatus; 028import org.hl7.fhir.r4.model.Factory; 029import org.hl7.fhir.r4.model.IntegerType; 030import org.hl7.fhir.r4.model.Quantity; 031import org.hl7.fhir.r4.model.Questionnaire; 032import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent; 033import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; 034import org.hl7.fhir.r4.model.QuestionnaireResponse; 035import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent; 036import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus; 037import org.hl7.fhir.r4.model.Reference; 038import org.hl7.fhir.r4.model.Resource; 039import org.hl7.fhir.r4.model.StringType; 040import org.hl7.fhir.r4.model.StructureDefinition; 041import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; 042import org.hl7.fhir.r4.model.TimeType; 043import org.hl7.fhir.r4.model.Type; 044import org.hl7.fhir.r4.model.UriType; 045import org.hl7.fhir.r4.model.ValueSet; 046import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent; 047import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; 048import org.hl7.fhir.r4.terminologies.ValueSetExpander; 049import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 050import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 051import org.hl7.fhir.utilities.Utilities; 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 * This class takes a profile, and builds a questionnaire from it 084 * 085 * If you then convert this questionnaire to a form using the XMLTools form 086 * builder, and then take the QuestionnaireResponse this creates, you can use 087 * QuestionnaireInstanceConvert to build an instance the conforms to the profile 088 * 089 * FHIR context: conceptLocator, codeSystems, valueSets, maps, client, profiles 090 * You don"t have to provide any of these, but the more you provide, the better 091 * the conversion will be 092 * 093 * @author Grahame 094 * 095 */ 096@MarkedToMoveToAdjunctPackage 097public class QuestionnaireBuilder { 098 099 private static final int MaxListboxCodings = 20; 100 private IWorkerContext context; 101 private int lastid = 0; 102 private Resource resource; 103 private StructureDefinition profile; 104 private Questionnaire questionnaire; 105 private QuestionnaireResponse response; 106 private String questionnaireId; 107 private Factory factory = new Factory(); 108 private Map<String, String> vsCache = new HashMap<String, String>(); 109 private ValueSetExpander expander; 110 111 // sometimes, when this is used, the questionnaire is already build and cached, 112 // and we are 113 // processing the response. for technical reasons, we still go through the 114 // 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.getType().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 List<ElementDefinition> list = new ArrayList<ElementDefinition>(); 196 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>(); 197 198 if (resource != null) 199 answerGroups.addAll(response.getItem()); 200 if (prebuiltQuestionnaire != null) { 201 // give it a fake group to build 202 Questionnaire.QuestionnaireItemComponent group = new Questionnaire.QuestionnaireItemComponent(); 203 group.setType(QuestionnaireItemType.GROUP); 204 buildGroup(group, profile, profile.getSnapshot().getElement().get(0), list, answerGroups); 205 } else 206 buildGroup(questionnaire.getItem().get(0), profile, profile.getSnapshot().getElement().get(0), list, 207 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 218 // we have a profile 219 if (prebuiltQuestionnaire == null) { 220 questionnaire.addIdentifier().setSystem("urn:ietf:rfc:3986").setValue(questionnaireId); 221 questionnaire.setVersion(profile.getVersion()); 222 questionnaire.setStatus(profile.getStatus()); 223 questionnaire.setDate(profile.getDate()); 224 questionnaire.setPublisher(profile.getPublisher()); 225 Questionnaire.QuestionnaireItemComponent item = new Questionnaire.QuestionnaireItemComponent(); 226 questionnaire.addItem(item); 227 item.getCode().addAll(profile.getKeyword()); 228 questionnaire.setId(nextId("qs")); 229 } 230 231 if (response != null) { 232 // no identifier - this is transient 233 response.setQuestionnaire("#" + questionnaire.getId()); 234 response.getContained().add(questionnaire); 235 response.setStatus(QuestionnaireResponseStatus.INPROGRESS); 236 QuestionnaireResponse.QuestionnaireResponseItemComponent item = new QuestionnaireResponse.QuestionnaireResponseItemComponent(); 237 response.addItem(item); 238 item.setUserData("object", resource); 239 } 240 241 } 242 243 private String nextId(String prefix) { 244 lastid++; 245 return prefix + Integer.toString(lastid); 246 } 247 248 private void buildGroup(QuestionnaireItemComponent group, StructureDefinition profile, ElementDefinition element, 249 List<ElementDefinition> parents, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) 250 throws FHIRException { 251 group.setLinkId(element.getPath()); // todo: this will be wrong when we start slicing 252 group.setText(element.getShort()); // todo - may need to prepend the name tail... 253 if (element.getComment() != null) { 254 Questionnaire.QuestionnaireItemComponent display = new Questionnaire.QuestionnaireItemComponent(); 255 display.setType(QuestionnaireItemType.DISPLAY); 256 display.setText(element.getComment()); 257 group.addItem(display); 258 } 259 group.setType(QuestionnaireItemType.GROUP); 260 ToolingExtensions.addFlyOver(group, element.getDefinition()); 261 group.setRequired(element.getMin() > 0); 262 if (element.getMin() > 0) 263 ToolingExtensions.addMin(group, element.getMin()); 264 group.setRepeats(!element.getMax().equals("1")); 265 if (!element.getMax().equals("*")) 266 ToolingExtensions.addMax(group, Integer.parseInt(element.getMax())); 267 268 for (org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) { 269 ag.setLinkId(group.getLinkId()); 270 ag.setText(group.getText()); 271 } 272 273 // now, we iterate the children 274 List<ElementDefinition> list = ProfileUtilities.getChildList(profile, element); 275 for (ElementDefinition child : list) { 276 277 if (!isExempt(element, child) && !parents.contains(child)) { 278 List<ElementDefinition> nparents = new ArrayList<ElementDefinition>(); 279 nparents.addAll(parents); 280 nparents.add(child); 281 QuestionnaireItemComponent childGroup = group.addItem(); 282 childGroup.setType(QuestionnaireItemType.GROUP); 283 284 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> nResponse = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>(); 285 processExisting(child.getPath(), answerGroups, nResponse); 286 // if the element has a type, we add a question. else we add a group on the 287 // basis that 288 // it will have children of its own 289 if (child.getType().isEmpty() || isAbstractType(child.getType())) 290 buildGroup(childGroup, profile, child, nparents, nResponse); 291 else if (isInlineDataType(child.getType())) 292 buildGroup(childGroup, profile, child, nparents, nResponse); // todo: get the right children for this one... 293 else 294 buildQuestion(childGroup, profile, child, child.getPath(), nResponse, parents); 295 } 296 } 297 } 298 299 private boolean isAbstractType(List<TypeRefComponent> type) { 300 return type.size() == 1 301 && (type.get(0).getWorkingCode().equals("Element") || type.get(0).getWorkingCode().equals("BackboneElement")); 302 } 303 304 private boolean isInlineDataType(List<TypeRefComponent> type) { 305 return type.size() == 1 && !Utilities.existsInList(type.get(0).getWorkingCode(), "code", "string", "id", "oid", 306 "markdown", "uri", "boolean", "decimal", "dateTime", "date", "instant", "time", "CodeableConcept", "Period", 307 "Ratio", "HumanName", "Address", "ContactPoint", "Identifier", "integer", "positiveInt", "unsignedInt", 308 "Coding", "Quantity", "Count", "Age", "Duration", "Distance", "Money", "Money", "Reference", "Duration", 309 "base64Binary", "Attachment", "Age", "Range", "Timing", "Annotation", "SampledData", "Extension", "SampledData", 310 "Narrative", "Resource", "Meta", "url", "canonical"); 311 } 312 313 private boolean isExempt(ElementDefinition element, ElementDefinition child) { 314 String n = tail(child.getPath()); 315 String t = ""; 316 if (!element.getType().isEmpty()) 317 t = element.getType().get(0).getWorkingCode(); 318 319 // we don't generate questions for the base stuff in every element 320 if (t.equals("Resource") && (n.equals("text") || n.equals("language") || n.equals("contained"))) 321 return true; 322 // we don't generate questions for extensions 323 else if (n.equals("extension") || n.equals("modifierExtension")) { 324 if (child.getType().size() > 0 && !child.getType().get(0).hasProfile()) 325 return false; 326 else 327 return true; 328 } else 329 return false; 330 } 331 332 private String tail(String path) { 333 return path.substring(path.lastIndexOf('.') + 1); 334 } 335 336 private void processExisting(String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, 337 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> nResponse) throws FHIRException { 338 // processing existing data 339 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) { 340 List<Base> children = ((Element) ag.getUserData("object")).listChildrenByName(tail(path)); 341 for (Base child : children) { 342 if (child != null) { 343 QuestionnaireResponse.QuestionnaireResponseItemComponent ans = ag.addItem(); 344 ans.setUserData("object", child); 345 nResponse.add(ans); 346 } 347 } 348 } 349 } 350 351 private void buildQuestion(QuestionnaireItemComponent group, StructureDefinition profile, ElementDefinition element, 352 String path, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, 353 List<ElementDefinition> parents) throws FHIRException { 354 group.setLinkId(path); 355 356 // in this context, we don't have any concepts to mark... 357 group.setText(element.getShort()); // prefix with name? 358 group.setRequired(element.getMin() > 0); 359 if (element.getMin() > 0) 360 ToolingExtensions.addMin(group, element.getMin()); 361 group.setRepeats(!element.getMax().equals('1')); 362 if (!element.getMax().equals("*")) 363 ToolingExtensions.addMax(group, Integer.parseInt(element.getMax())); 364 365 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) { 366 ag.setLinkId(group.getLinkId()); 367 ag.setText(group.getText()); 368 } 369 370 if (!Utilities.noString(element.getComment())) 371 ToolingExtensions.addFlyOver(group, element.getDefinition() + " " + element.getComment()); 372 else 373 ToolingExtensions.addFlyOver(group, element.getDefinition()); 374 375 if (element.getType().size() > 1 || element.getType().get(0).getWorkingCode().equals("*")) { 376 List<TypeRefComponent> types = expandTypeList(element.getType()); 377 Questionnaire.QuestionnaireItemComponent q = addQuestion(group, QuestionnaireItemType.CHOICE, element.getPath(), 378 "_type", "type", null, makeTypeList(profile, types, element.getPath())); 379 for (TypeRefComponent t : types) { 380 Questionnaire.QuestionnaireItemComponent sub = q.addItem(); 381 sub.setType(QuestionnaireItemType.GROUP); 382 sub.setLinkId(element.getPath() + "._" + t.getUserData("text")); 383 sub.setText((String) t.getUserData("text")); 384 // always optional, never repeats 385 386 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> selected = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>(); 387 selectTypes(profile, sub, t, answerGroups, selected); 388 processDataType(profile, sub, element, element.getPath() + "._" + t.getUserData("text"), t, selected, parents); 389 } 390 } else 391 // now we have to build the question panel for each different data type 392 processDataType(profile, group, element, element.getPath(), element.getType().get(0), answerGroups, parents); 393 394 } 395 396 private List<TypeRefComponent> expandTypeList(List<TypeRefComponent> types) { 397 List<TypeRefComponent> result = new ArrayList<TypeRefComponent>(); 398 for (TypeRefComponent t : types) { 399 if (t.hasProfile()) 400 result.add(t); 401 else if (t.getWorkingCode().equals("*")) { 402 result.add(new TypeRefComponent().setCode("integer")); 403 result.add(new TypeRefComponent().setCode("decimal")); 404 result.add(new TypeRefComponent().setCode("dateTime")); 405 result.add(new TypeRefComponent().setCode("date")); 406 result.add(new TypeRefComponent().setCode("instant")); 407 result.add(new TypeRefComponent().setCode("time")); 408 result.add(new TypeRefComponent().setCode("string")); 409 result.add(new TypeRefComponent().setCode("uri")); 410 result.add(new TypeRefComponent().setCode("boolean")); 411 result.add(new TypeRefComponent().setCode("Coding")); 412 result.add(new TypeRefComponent().setCode("CodeableConcept")); 413 result.add(new TypeRefComponent().setCode("Attachment")); 414 result.add(new TypeRefComponent().setCode("Identifier")); 415 result.add(new TypeRefComponent().setCode("Quantity")); 416 result.add(new TypeRefComponent().setCode("Range")); 417 result.add(new TypeRefComponent().setCode("Period")); 418 result.add(new TypeRefComponent().setCode("Ratio")); 419 result.add(new TypeRefComponent().setCode("HumanName")); 420 result.add(new TypeRefComponent().setCode("Address")); 421 result.add(new TypeRefComponent().setCode("ContactPoint")); 422 result.add(new TypeRefComponent().setCode("Timing")); 423 result.add(new TypeRefComponent().setCode("Reference")); 424 } else 425 result.add(t); 426 } 427 return result; 428 } 429 430 private ValueSet makeTypeList(StructureDefinition profile, List<TypeRefComponent> types, String path) { 431 ValueSet vs = new ValueSet(); 432 vs.setName("Type options for " + path); 433 vs.setDescription(vs.present()); 434 vs.setStatus(PublicationStatus.ACTIVE); 435 vs.setExpansion(new ValueSetExpansionComponent()); 436 vs.getExpansion().setIdentifier(Factory.createUUID()); 437 vs.getExpansion().setTimestampElement(DateTimeType.now()); 438 for (TypeRefComponent t : types) { 439 if (t.hasTarget()) { 440 for (UriType u : t.getTargetProfile()) { 441 if (u.getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) { 442 ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains(); 443 cc.setCode(u.getValue().substring(40)); 444 cc.setSystem("http://hl7.org/fhir/resource-types"); 445 cc.setDisplay(cc.getCode()); 446 } 447 } 448 } else if (!t.hasProfile()) { 449 ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains(); 450 cc.setCode(t.getWorkingCode()); 451 cc.setDisplay(t.getWorkingCode()); 452 cc.setSystem("http://hl7.org/fhir/data-types"); 453 } else 454 for (UriType u : t.getProfile()) { 455 ProfileUtilities pu = new ProfileUtilities(context, null, null); 456 StructureDefinition ps = pu.getProfile(profile, u.getValue()); 457 if (ps != null) { 458 ValueSetExpansionContainsComponent cc = vs.getExpansion().addContains(); 459 cc.setCode(u.getValue()); 460 cc.setDisplay(ps.getType()); 461 cc.setSystem("http://hl7.org/fhir/resource-types"); 462 } 463 } 464 } 465 466 return vs; 467 } 468 469 private void selectTypes(StructureDefinition profile, QuestionnaireItemComponent sub, TypeRefComponent t, 470 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> source, 471 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> dest) throws FHIRFormatError { 472 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> temp = new ArrayList<QuestionnaireResponse.QuestionnaireResponseItemComponent>(); 473 474 for (QuestionnaireResponse.QuestionnaireResponseItemComponent g : source) 475 if (instanceOf(t, (Element) g.getUserData("object"))) 476 temp.add(g); 477 for (QuestionnaireResponse.QuestionnaireResponseItemComponent g : temp) 478 source.remove(g); 479 for (QuestionnaireResponse.QuestionnaireResponseItemComponent g : temp) { 480 // 1st the answer: 481 assert (g.getItem().size() == 0); // it should be empty 482 QuestionnaireResponse.QuestionnaireResponseItemComponent q = g.addItem(); 483 q.setLinkId(g.getLinkId() + "._type"); 484 q.setText("type"); 485 486 QuestionnaireResponseItemAnswerComponent a = q.addAnswer(); 487 if (t.hasTarget()) { 488 for (UriType u : t.getTargetProfile()) { 489 if (u.getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) { 490 Coding cc = new Coding(); 491 a.setValue(cc); 492 cc.setCode(u.getValue().substring(40)); 493 cc.setSystem("http://hl7.org/fhir/resource-types"); 494 } 495 } 496 } else { 497 Coding cc = new Coding(); 498 a.setValue(cc); 499 ProfileUtilities pu = new ProfileUtilities(context, null, null); 500 StructureDefinition ps = null; 501 if (t.hasProfile()) 502 ps = pu.getProfile(profile, t.getProfile().get(0).getValue()); 503 504 if (ps != null) { 505 cc.setCode(t.getProfile().get(0).getValue()); 506 cc.setSystem("http://hl7.org/fhir/resource-types"); 507 } else { 508 cc.setCode(t.getWorkingCode()); 509 cc.setSystem("http://hl7.org/fhir/data-types"); 510 } 511 } 512 513 // 1st: create the subgroup 514 QuestionnaireResponse.QuestionnaireResponseItemComponent subg = a.addItem(); 515 dest.add(subg); 516 subg.setLinkId(sub.getLinkId()); 517 subg.setText(sub.getText()); 518 subg.setUserData("object", g.getUserData("object")); 519 } 520 } 521 522 private boolean instanceOf(TypeRefComponent t, Element obj) { 523 if (t.getWorkingCode().equals("Reference")) { 524 if (!(obj instanceof Reference)) { 525 return false; 526 } else { 527 String url = ((Reference) obj).getReference(); 528 // there are several problems here around profile matching. This process is 529 // degenerative, and there's probably nothing we can do to solve it 530 if (url.startsWith("http:") || url.startsWith("https:")) 531 return true; 532 else if (t.hasProfile() 533 && t.getProfile().get(0).getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) 534 return url.startsWith(t.getProfile().get(0).getValue().substring(40) + '/'); 535 else 536 return true; 537 } 538 } else if (t.getWorkingCode().equals("Quantity")) { 539 return obj instanceof Quantity; 540 } else 541 throw new NotImplementedException("Not Done Yet"); 542 } 543 544 private QuestionnaireItemComponent addQuestion(QuestionnaireItemComponent group, QuestionnaireItemType af, 545 String path, String id, String name, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) 546 throws FHIRException { 547 return addQuestion(group, af, path, id, name, answerGroups, null); 548 } 549 550 private QuestionnaireItemComponent addQuestion(QuestionnaireItemComponent group, QuestionnaireItemType af, 551 String path, String id, String name, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, 552 ValueSet vs) throws FHIRException { 553 QuestionnaireItemComponent result = group.addItem(); 554 if (vs != null) { 555 if (vs.getExpansion() == null) { 556 result.setAnswerValueSet(vs.getUrl()); 557 ToolingExtensions.addControl(result, "lookup"); 558 } else { 559 if (Utilities.noString(vs.getId())) { 560 vs.setId(nextId("vs")); 561 questionnaire.getContained().add(vs); 562 vsCache.put(vs.getUrl(), vs.getId()); 563 vs.setText(null); 564 vs.setCompose(null); 565 vs.getContact().clear(); 566 vs.setPublisherElement(null); 567 vs.setCopyrightElement(null); 568 } 569 result.setAnswerValueSet("#" + vs.getId()); 570 } 571 } 572 573 result.setLinkId(path + '.' + id); 574 result.setText(name); 575 result.setType(af); 576 result.setRequired(false); 577 result.setRepeats(false); 578 if (id.endsWith("/1")) 579 id = id.substring(0, id.length() - 2); 580 581 if (answerGroups != null) { 582 583 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) { 584 List<Base> children = new ArrayList<Base>(); 585 586 QuestionnaireResponse.QuestionnaireResponseItemComponent aq = null; 587 Element obj = (Element) ag.getUserData("object"); 588 if (isPrimitive((TypeRefComponent) obj)) 589 children.add(obj); 590 else if (obj instanceof Enumeration) { 591 String value = ((Enumeration) obj).toString(); 592 children.add(new StringType(value)); 593 } else 594 children = obj.listChildrenByName(id); 595 596 for (Base child : children) { 597 if (child != null) { 598 if (aq == null) { 599 aq = ag.addItem(); 600 aq.setLinkId(result.getLinkId()); 601 aq.setText(result.getText()); 602 } 603 aq.addAnswer().setValue(convertType(child, af, vs, result.getLinkId())); 604 } 605 } 606 } 607 } 608 return result; 609 } 610 611 @SuppressWarnings("unchecked") 612 private Type convertType(Base value, QuestionnaireItemType af, ValueSet vs, String path) throws FHIRException { 613 switch (af) { 614 // simple cases 615 case BOOLEAN: 616 if (value instanceof BooleanType) 617 return (Type) value; 618 break; 619 case DECIMAL: 620 if (value instanceof DecimalType) 621 return (Type) value; 622 break; 623 case INTEGER: 624 if (value instanceof IntegerType) 625 return (Type) value; 626 break; 627 case DATE: 628 if (value instanceof DateType) 629 return (Type) value; 630 break; 631 case DATETIME: 632 if (value instanceof DateTimeType) 633 return (Type) value; 634 break; 635 case TIME: 636 if (value instanceof TimeType) 637 return (Type) value; 638 break; 639 case STRING: 640 if (value instanceof StringType) 641 return (Type) value; 642 else if (value instanceof UriType) 643 return new StringType(((UriType) value).asStringValue()); 644 break; 645 case TEXT: 646 if (value instanceof StringType) 647 return (Type) value; 648 break; 649 case QUANTITY: 650 if (value instanceof Quantity) 651 return (Type) value; 652 break; 653 654 // complex cases: 655 // ? QuestionnaireItemTypeAttachment: ...? 656 case CHOICE: 657 case OPENCHOICE: 658 if (value instanceof Coding) 659 return (Type) value; 660 else if (value instanceof Enumeration) { 661 Coding cc = new Coding(); 662 cc.setCode(((Enumeration<Enum<?>>) value).asStringValue()); 663 cc.setSystem(getSystemForCode(vs, cc.getCode(), path)); 664 return cc; 665 } else if (value instanceof StringType) { 666 Coding cc = new Coding(); 667 cc.setCode(((StringType) value).asStringValue()); 668 cc.setSystem(getSystemForCode(vs, cc.getCode(), path)); 669 return cc; 670 } 671 break; 672 673 case REFERENCE: 674 if (value instanceof Reference) 675 return (Type) value; 676 else if (value instanceof StringType) { 677 Reference r = new Reference(); 678 r.setReference(((StringType) value).asStringValue()); 679 } 680 break; 681 default: 682 break; 683 } 684 685 throw new FHIRException("Unable to convert from '" + value.getClass().toString() + "' for Answer Format " 686 + af.toCode() + ", path = " + path); 687 } 688 689 private String getSystemForCode(ValueSet vs, String code, String path) throws FHIRException { 690// var 691// i, q : integer; 692// begin 693 String result = null; 694 if (vs == null) { 695 if (prebuiltQuestionnaire == null) 696 throw new FHIRException("Logic error at path = " + path); 697 for (Resource r : prebuiltQuestionnaire.getContained()) { 698 if (r instanceof ValueSet) { 699 vs = (ValueSet) r; 700 if (vs.hasExpansion()) { 701 for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { 702 if (c.getCode().equals(code)) { 703 if (result == null) 704 result = c.getSystem(); 705 else 706 throw new FHIRException( 707 "Multiple matches in " + vs.getUrl() + " for code " + code + " at path = " + path); 708 } 709 } 710 } 711 } 712 } 713 } 714 715 for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { 716 if (c.getCode().equals(code)) { 717 if (result == null) 718 result = c.getSystem(); 719 else 720 throw new FHIRException("Multiple matches in " + vs.getUrl() + " for code " + code + " at path = " + path); 721 } 722 } 723 if (result != null) 724 return result; 725 throw new FHIRException("Unable to resolve code " + code + " at path = " + path); 726 } 727 728 private boolean isPrimitive(TypeRefComponent t) { 729 String code = t.getWorkingCode(); 730 StructureDefinition sd = context.fetchTypeDefinition(code); 731 return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE; 732 } 733 734 private void processDataType(StructureDefinition profile, QuestionnaireItemComponent group, ElementDefinition element, 735 String path, TypeRefComponent t, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, 736 List<ElementDefinition> parents) throws FHIRException { 737 String tc = t.getWorkingCode(); 738 if (tc.equals("code")) 739 addCodeQuestions(group, element, path, answerGroups); 740 else if (Utilities.existsInList(tc, "string", "id", "oid", "uuid", "markdown")) 741 addStringQuestions(group, element, path, answerGroups); 742 else if (Utilities.existsInList(tc, "uri", "url", "canonical")) 743 addUriQuestions(group, element, path, answerGroups); 744 else if (tc.equals("boolean")) 745 addBooleanQuestions(group, element, path, answerGroups); 746 else if (tc.equals("decimal")) 747 addDecimalQuestions(group, element, path, answerGroups); 748 else if (tc.equals("dateTime") || tc.equals("date")) 749 addDateTimeQuestions(group, element, path, answerGroups); 750 else if (tc.equals("instant")) 751 addInstantQuestions(group, element, path, answerGroups); 752 else if (tc.equals("time")) 753 addTimeQuestions(group, element, path, answerGroups); 754 else if (tc.equals("CodeableConcept")) 755 addCodeableConceptQuestions(group, element, path, answerGroups); 756 else if (tc.equals("Period")) 757 addPeriodQuestions(group, element, path, answerGroups); 758 else if (tc.equals("Ratio")) 759 addRatioQuestions(group, element, path, answerGroups); 760 else if (tc.equals("HumanName")) 761 addHumanNameQuestions(group, element, path, answerGroups); 762 else if (tc.equals("Address")) 763 addAddressQuestions(group, element, path, answerGroups); 764 else if (tc.equals("ContactPoint")) 765 addContactPointQuestions(group, element, path, answerGroups); 766 else if (tc.equals("Identifier")) 767 addIdentifierQuestions(group, element, path, answerGroups); 768 else if (tc.equals("integer") || tc.equals("positiveInt") || tc.equals("unsignedInt")) 769 addIntegerQuestions(group, element, path, answerGroups); 770 else if (tc.equals("Coding")) 771 addCodingQuestions(group, element, path, answerGroups); 772 else if (Utilities.existsInList(tc, "Quantity", "Count", "Age", "Duration", "Distance", "Money")) 773 addQuantityQuestions(group, element, path, answerGroups); 774 else if (tc.equals("Money")) 775 addMoneyQuestions(group, element, path, answerGroups); 776 else if (tc.equals("Reference")) 777 addReferenceQuestions(group, element, path, t.getTargetProfile(), answerGroups); 778 else if (tc.equals("Duration")) 779 addDurationQuestions(group, element, path, answerGroups); 780 else if (tc.equals("base64Binary")) 781 addBinaryQuestions(group, element, path, answerGroups); 782 else if (tc.equals("Attachment")) 783 addAttachmentQuestions(group, element, path, answerGroups); 784 else if (tc.equals("Age")) 785 addAgeQuestions(group, element, path, answerGroups); 786 else if (tc.equals("Range")) 787 addRangeQuestions(group, element, path, answerGroups); 788 else if (tc.equals("Timing")) 789 addTimingQuestions(group, element, path, answerGroups); 790 else if (tc.equals("Annotation")) 791 addAnnotationQuestions(group, element, path, answerGroups); 792 else if (tc.equals("SampledData")) 793 addSampledDataQuestions(group, element, path, answerGroups); 794 else if (tc.equals("Extension")) { 795 if (t.hasProfile()) 796 addExtensionQuestions(profile, group, element, path, t.getProfile().get(0).getValue(), answerGroups, parents); 797 } else if (tc.equals("SampledData")) 798 addSampledDataQuestions(group, element, path, answerGroups); 799 else if (!tc.equals("Narrative") && !tc.equals("Resource") && !tc.equals("Meta") && !tc.equals("Signature")) { 800 StructureDefinition sd = context.fetchTypeDefinition(tc); 801 if (sd == null) 802 throw new NotImplementedException("Unhandled Data Type: " + tc + " on element " + element.getPath()); 803 buildGroup(group, sd, sd.getSnapshot().getElementFirstRep(), parents, answerGroups); 804 } 805 } 806 807 private void addCodeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 808 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 809 ToolingExtensions.addFhirType(group, "code"); 810 ValueSet vs = resolveValueSet(null, element.hasBinding() ? element.getBinding() : null); 811 addQuestion(group, QuestionnaireItemType.CHOICE, path, "value", unCamelCase(tail(element.getPath())), answerGroups, 812 vs); 813 group.setText(null); 814 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 815 ag.setText(null); 816 } 817 818 private String unCamelCase(String s) { 819 StringBuilder result = new StringBuilder(); 820 821 for (int i = 0; i < s.length(); i++) { 822 if (Character.isUpperCase(s.charAt(i))) 823 result.append(' '); 824 result.append(s.charAt(i)); 825 } 826 return result.toString().toLowerCase(); 827 } 828 829 private void addStringQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 830 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 831 ToolingExtensions.addFhirType(group, "string"); 832 addQuestion(group, QuestionnaireItemType.STRING, path, "value", group.getText(), answerGroups); 833 group.setText(null); 834 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 835 ag.setText(null); 836 } 837 838 private void addTimeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 839 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 840 ToolingExtensions.addFhirType(group, "time"); 841 addQuestion(group, QuestionnaireItemType.TIME, path, "value", group.getText(), answerGroups); 842 group.setText(null); 843 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 844 ag.setText(null); 845 } 846 847 private void addUriQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 848 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 849 ToolingExtensions.addFhirType(group, "uri"); 850 addQuestion(group, QuestionnaireItemType.STRING, path, "value", group.getText(), answerGroups); 851 group.setText(null); 852 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 853 ag.setText(null); 854 } 855 856 private void addBooleanQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 857 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 858 ToolingExtensions.addFhirType(group, "boolean"); 859 addQuestion(group, QuestionnaireItemType.BOOLEAN, path, "value", group.getText(), answerGroups); 860 group.setText(null); 861 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 862 ag.setText(null); 863 } 864 865 private void addDecimalQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 866 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 867 ToolingExtensions.addFhirType(group, "decimal"); 868 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", group.getText(), answerGroups); 869 group.setText(null); 870 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 871 ag.setText(null); 872 } 873 874 private void addIntegerQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 875 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 876 ToolingExtensions.addFhirType(group, "integer"); 877 addQuestion(group, QuestionnaireItemType.INTEGER, path, "value", group.getText(), answerGroups); 878 group.setText(null); 879 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 880 ag.setText(null); 881 } 882 883 private void addDateTimeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 884 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 885 ToolingExtensions.addFhirType(group, "datetime"); 886 addQuestion(group, QuestionnaireItemType.DATETIME, path, "value", group.getText(), answerGroups); 887 group.setText(null); 888 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 889 ag.setText(null); 890 } 891 892 private void addInstantQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 893 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 894 ToolingExtensions.addFhirType(group, "instant"); 895 addQuestion(group, QuestionnaireItemType.DATETIME, path, "value", group.getText(), answerGroups); 896 group.setText(null); 897 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 898 ag.setText(null); 899 } 900 901 private void addBinaryQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 902 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) { 903 ToolingExtensions.addFhirType(group, "binary"); 904 // ? Lloyd: how to support binary content 905 } 906 907 // Complex Types --------------------------------------------------------------- 908 909 private QuestionnaireItemType answerTypeForBinding(ElementDefinitionBindingComponent binding) { 910 if (binding == null) 911 return QuestionnaireItemType.OPENCHOICE; 912 else if (binding.getStrength() != BindingStrength.REQUIRED) 913 return QuestionnaireItemType.OPENCHOICE; 914 else 915 return QuestionnaireItemType.CHOICE; 916 } 917 918 private void addCodingQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 919 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 920 ToolingExtensions.addFhirType(group, "Coding"); 921 addQuestion(group, answerTypeForBinding(element.hasBinding() ? element.getBinding() : null), path, "value", 922 group.getText(), answerGroups, resolveValueSet(null, element.hasBinding() ? element.getBinding() : null)); 923 group.setText(null); 924 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 925 ag.setText(null); 926 } 927 928 private void addCodeableConceptQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 929 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 930 ToolingExtensions.addFhirType(group, "CodeableConcept"); 931 addQuestion(group, answerTypeForBinding(element.hasBinding() ? element.getBinding() : null), path, "coding", 932 "code:", answerGroups, resolveValueSet(null, element.hasBinding() ? element.getBinding() : null)); 933 addQuestion(group, QuestionnaireItemType.STRING, path, "text", "text:", answerGroups); 934 } 935 936 private ValueSet makeAnyValueSet() { 937 // TODO Auto-generated method stub 938 return null; 939 } 940 941 private void addPeriodQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 942 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 943 ToolingExtensions.addFhirType(group, "Period"); 944 addQuestion(group, QuestionnaireItemType.DATETIME, path, "low", "start:", answerGroups); 945 addQuestion(group, QuestionnaireItemType.DATETIME, path, "end", "end:", answerGroups); 946 } 947 948 private void addRatioQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 949 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 950 ToolingExtensions.addFhirType(group, "Ratio"); 951 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "numerator", "numerator:", answerGroups); 952 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "denominator", "denominator:", answerGroups); 953 addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups); 954 } 955 956 private void addHumanNameQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 957 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 958 ToolingExtensions.addFhirType(group, "Name"); 959 addQuestion(group, QuestionnaireItemType.STRING, path, "text", "text:", answerGroups); 960 addQuestion(group, QuestionnaireItemType.STRING, path, "family", "family:", answerGroups).setRepeats(true); 961 addQuestion(group, QuestionnaireItemType.STRING, path, "given", "given:", answerGroups).setRepeats(true); 962 } 963 964 private void addAddressQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 965 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 966 ToolingExtensions.addFhirType(group, "Address"); 967 addQuestion(group, QuestionnaireItemType.STRING, path, "text", "text:", answerGroups); 968 addQuestion(group, QuestionnaireItemType.STRING, path, "line", "line:", answerGroups).setRepeats(true); 969 addQuestion(group, QuestionnaireItemType.STRING, path, "city", "city:", answerGroups); 970 addQuestion(group, QuestionnaireItemType.STRING, path, "state", "state:", answerGroups); 971 addQuestion(group, QuestionnaireItemType.STRING, path, "postalCode", "post code:", answerGroups); 972 addQuestion(group, QuestionnaireItemType.STRING, path, "country", "country:", answerGroups); 973 addQuestion(group, QuestionnaireItemType.CHOICE, path, "use", "use:", answerGroups, 974 resolveValueSet("http://hl7.org/fhir/vs/address-use")); 975 } 976 977 private void addContactPointQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 978 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 979 ToolingExtensions.addFhirType(group, "ContactPoint"); 980 addQuestion(group, QuestionnaireItemType.CHOICE, path, "system", "type:", answerGroups, 981 resolveValueSet("http://hl7.org/fhir/vs/contact-point-system")); 982 addQuestion(group, QuestionnaireItemType.STRING, path, "value", "value:", answerGroups); 983 addQuestion(group, QuestionnaireItemType.CHOICE, path, "use", "use:", answerGroups, 984 resolveValueSet("http://hl7.org/fhir/vs/contact-point-use")); 985 } 986 987 private void addIdentifierQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 988 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 989 ToolingExtensions.addFhirType(group, "Identifier"); 990 addQuestion(group, QuestionnaireItemType.STRING, path, "label", "label:", answerGroups); 991 addQuestion(group, QuestionnaireItemType.STRING, path, "system", "system:", answerGroups); 992 addQuestion(group, QuestionnaireItemType.STRING, path, "value", "value:", answerGroups); 993 } 994 995 private void addSimpleQuantityQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 996 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 997 ToolingExtensions.addFhirType(group, "Quantity"); 998 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups); 999 addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups); 1000 addQuestion(group, QuestionnaireItemType.STRING, path, "code", "coded units:", answerGroups); 1001 addQuestion(group, QuestionnaireItemType.STRING, path, "system", "units system:", answerGroups); 1002 } 1003 1004 private void addQuantityQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1005 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 1006 ToolingExtensions.addFhirType(group, "Quantity"); 1007 addQuestion(group, QuestionnaireItemType.CHOICE, path, "comparator", "comp:", answerGroups, 1008 resolveValueSet("http://hl7.org/fhir/vs/quantity-comparator")); 1009 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups); 1010 addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups); 1011 addQuestion(group, QuestionnaireItemType.STRING, path, "code", "coded units:", answerGroups); 1012 addQuestion(group, QuestionnaireItemType.STRING, path, "system", "units system:", answerGroups); 1013 } 1014 1015 private void addMoneyQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1016 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 1017 ToolingExtensions.addFhirType(group, "Money"); 1018 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups); 1019 addQuestion(group, QuestionnaireItemType.STRING, path, "currency", "currency:", answerGroups); 1020 } 1021 1022 private void addAgeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1023 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 1024 ToolingExtensions.addFhirType(group, "Age"); 1025 addQuestion(group, QuestionnaireItemType.CHOICE, path, "comparator", "comp:", answerGroups, 1026 resolveValueSet("http://hl7.org/fhir/vs/quantity-comparator")); 1027 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups); 1028 addQuestion(group, QuestionnaireItemType.CHOICE, path, "units", "units:", answerGroups, 1029 resolveValueSet("http://hl7.org/fhir/vs/duration-units")); 1030 } 1031 1032 private void addDurationQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1033 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 1034 ToolingExtensions.addFhirType(group, "Duration"); 1035 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "value", "value:", answerGroups); 1036 addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups); 1037 } 1038 1039 private void addAttachmentQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1040 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) { 1041 ToolingExtensions.addFhirType(group, "Attachment"); 1042 // raise Exception.Create("addAttachmentQuestions not Done Yet"); 1043 } 1044 1045 private void addRangeQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1046 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 1047 ToolingExtensions.addFhirType(group, "Range"); 1048 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "low", "low:", answerGroups); 1049 addQuestion(group, QuestionnaireItemType.DECIMAL, path, "high", "high:", answerGroups); 1050 addQuestion(group, QuestionnaireItemType.STRING, path, "units", "units:", answerGroups); 1051 } 1052 1053 private void addSampledDataQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1054 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) { 1055 ToolingExtensions.addFhirType(group, "SampledData"); 1056 } 1057 1058 private void addTimingQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1059 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) throws FHIRException { 1060 ToolingExtensions.addFhirType(group, "Schedule"); 1061 addQuestion(group, QuestionnaireItemType.STRING, path, "text", "text:", answerGroups); 1062 addQuestion(group, QuestionnaireItemType.DATETIME, path, "date", "date:", answerGroups); 1063 QuestionnaireItemComponent q = addQuestion(group, QuestionnaireItemType.REFERENCE, path, "author", "author:", 1064 answerGroups); 1065 ToolingExtensions.addAllowedResource(q, "Patient"); 1066 ToolingExtensions.addAllowedResource(q, "Practitioner"); 1067 ToolingExtensions.addAllowedResource(q, "RelatedPerson"); 1068 } 1069 1070 private void addAnnotationQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1071 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) { 1072 ToolingExtensions.addFhirType(group, "Annotation"); 1073 } 1074 // Special Types --------------------------------------------------------------- 1075 1076 private void addReferenceQuestions(QuestionnaireItemComponent group, ElementDefinition element, String path, 1077 List<CanonicalType> profileURL, List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups) 1078 throws FHIRException { 1079 // var 1080 // rn : String; 1081 // i : integer; 1082 // q : TFhirQuestionnaireGroupQuestion; 1083 ToolingExtensions.addFhirType(group, "Reference"); 1084 1085 QuestionnaireItemComponent q = addQuestion(group, QuestionnaireItemType.REFERENCE, path, "value", group.getText(), 1086 answerGroups); 1087 group.setText(null); 1088 CommaSeparatedStringBuilder rn = new CommaSeparatedStringBuilder(); 1089 for (UriType u : profileURL) 1090 if (u.getValue().startsWith("http://hl7.org/fhir/StructureDefinition/")) 1091 rn.append(u.getValue().substring(40)); 1092 if (rn.length() == 0) 1093 ToolingExtensions.addReferenceFilter(q, "subject=$subj&patient=$subj&encounter=$encounter"); 1094 else { 1095 ToolingExtensions.addAllowedResource(q, rn.toString()); 1096 ToolingExtensions.addReferenceFilter(q, "subject=$subj&patient=$subj&encounter=$encounter"); 1097 } 1098 for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) 1099 ag.setText(null); 1100 } 1101 1102 private void addExtensionQuestions(StructureDefinition profile, QuestionnaireItemComponent group, 1103 ElementDefinition element, String path, String url, 1104 List<QuestionnaireResponse.QuestionnaireResponseItemComponent> answerGroups, List<ElementDefinition> parents) 1105 throws FHIRException { 1106 // if this a profiled extension, then we add it 1107 if (!Utilities.noString(url)) { 1108 StructureDefinition ed = context.fetchResource(StructureDefinition.class, url); 1109 if (ed != null) { 1110 if (answerGroups.size() > 0) 1111 throw new NotImplementedException("Debug this"); 1112 buildQuestion(group, profile, ed.getSnapshot().getElement().get(0), path + ".extension[" + url + "]", 1113 answerGroups, parents); 1114 } 1115 } 1116 } 1117 1118 private ValueSet resolveValueSet(String url) { 1119// if (prebuiltQuestionnaire != null) 1120 return null; // we don't do anything with value sets in this case 1121 1122// if (vsCache.containsKey(url)) 1123// return (ValueSet) questionnaire.getContained(vsCache.get(url)); 1124// else { 1125// ValueSet vs = context.findValueSet(url); 1126// if (vs != null) 1127// return expander.expand(vs, MaxListboxCodings, false); 1128// } 1129// 1130// /* on e: ETooCostly do 1131// begin 1132// result := TFhirValueSet.Create; 1133// try 1134// result.identifierST := ref.referenceST; 1135// result.link; 1136// finally 1137// result.Free; 1138// end; 1139// end; 1140// on e : Exception do 1141// raise; 1142// end;*/ 1143// } 1144 } 1145 1146 private ValueSet resolveValueSet(Object object, ElementDefinitionBindingComponent binding) { 1147 return null; 1148 } 1149 1150}