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