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