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