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