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