![](/hapi-fhir/images/logos/raccoon-forwards.png)
001package org.hl7.fhir.r5.renderers; 002 003import java.io.ByteArrayOutputStream; 004import java.io.IOException; 005import java.io.UnsupportedEncodingException; 006import java.nio.charset.Charset; 007import java.util.ArrayList; 008import java.util.HashMap; 009import java.util.List; 010import java.util.Map; 011 012import org.hl7.fhir.exceptions.DefinitionException; 013import org.hl7.fhir.exceptions.FHIRException; 014import org.hl7.fhir.exceptions.FHIRFormatError; 015import org.hl7.fhir.r5.context.ContextUtilities; 016import org.hl7.fhir.r5.model.CanonicalType; 017import org.hl7.fhir.r5.model.Enumerations; 018import org.hl7.fhir.r5.model.ExampleScenario; 019import org.hl7.fhir.r5.model.ExampleScenario.ExampleScenarioActorComponent; 020import org.hl7.fhir.r5.model.ExampleScenario.ExampleScenarioInstanceComponent; 021import org.hl7.fhir.r5.model.ExampleScenario.ExampleScenarioInstanceContainedInstanceComponent; 022import org.hl7.fhir.r5.model.ExampleScenario.ExampleScenarioInstanceVersionComponent; 023import org.hl7.fhir.r5.model.ExampleScenario.ExampleScenarioProcessComponent; 024import org.hl7.fhir.r5.model.ExampleScenario.ExampleScenarioProcessStepAlternativeComponent; 025import org.hl7.fhir.r5.model.ExampleScenario.ExampleScenarioProcessStepComponent; 026import org.hl7.fhir.r5.model.ExampleScenario.ExampleScenarioProcessStepOperationComponent; 027import org.hl7.fhir.r5.model.Resource; 028import org.hl7.fhir.r5.model.StructureDefinition; 029import org.hl7.fhir.r5.renderers.utils.RenderingContext; 030import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; 031import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 032import org.hl7.fhir.r5.utils.EOperationOutcome; 033import org.hl7.fhir.utilities.xhtml.XhtmlDocument; 034import org.hl7.fhir.utilities.xhtml.XhtmlNode; 035 036import net.sourceforge.plantuml.FileFormat; 037import net.sourceforge.plantuml.FileFormatOption; 038import net.sourceforge.plantuml.SourceStringReader; 039 040public class ExampleScenarioRenderer extends TerminologyRenderer { 041 042 public ExampleScenarioRenderer(RenderingContext context) { 043 super(context); 044 } 045 046 @Override 047 public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { 048 if (r.isDirect()) { 049 renderResourceTechDetails(r, x); 050 genSummaryTable(status, x, (ExampleScenario) r.getBase()); 051 render(status, x, (ExampleScenario) r.getBase(), r); 052 } else { 053 throw new Error("ExampleScenarioRenderer only renders native resources directly"); 054 } 055 } 056 057 @Override 058 public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException { 059 return canonicalTitle(r); 060 } 061 062 public void render(RenderingStatus status, XhtmlNode x, ExampleScenario scen, ResourceWrapper res) throws FHIRException { 063 try { 064 if (context.getScenarioMode() == null) { 065 renderActors(status, res, x, scen); 066 } else { 067 switch (context.getScenarioMode()) { 068 case ACTORS: 069 renderActors(status, res, x, scen); 070 break; 071 case INSTANCES: 072 renderInstances(status, res, x, scen); 073 break; 074 case PROCESSES: 075 renderProcesses(status, x, scen); 076 break; 077 default: 078 throw new FHIRException(context.formatPhrase(RenderingContext.EX_SCEN_UN, context.getScenarioMode()) + " "); 079 } 080 } 081 } catch (Exception e) { 082 throw new FHIRException(context.formatPhrase(RenderingContext.EX_SCEN_ERR_REN, scen.getUrl(), e) + " "); 083 } 084 } 085 086 public String renderDiagram(RenderingStatus status, ResourceWrapper res, ExampleScenario scen) throws IOException { 087 String plantUml = toPlantUml(status, res, scen); 088 SourceStringReader reader = new SourceStringReader(plantUml); 089 final ByteArrayOutputStream os = new ByteArrayOutputStream(); 090 reader.outputImage(os, new FileFormatOption(FileFormat.SVG)); 091 os.close(); 092 093 final String svg = new String(os.toByteArray(), Charset.forName("UTF-8")); 094 return svg; 095 } 096 097 protected String toPlantUml(RenderingStatus status, ResourceWrapper res, ExampleScenario scen) throws IOException { 098 String plantUml = "@startuml\r\n"; 099 plantUml += "Title " + (scen.hasTitle() ? scen.getTitle() : scen.getName()) + "\r\n\r\n"; 100 Map<String, String> actorKeys = new HashMap<String, String>(); 101 102 for (ExampleScenarioActorComponent actor: scen.getActor()) { 103 String actorType = actor.getType().equals(Enumerations.ExampleScenarioActorType.PERSON) ? "actor" : "participant"; 104 actorKeys.put(actor.getKey(), escapeKey(actor.getKey())); 105 plantUml += actorType + " \"" + creolLink(actor.getTitle(), "#a_" + actor.getKey(), actor.getDescription()) + "\" as " + actorKeys.get(actor.getKey()) + "\r\n"; 106 } 107 plantUml += "\r\n"; 108 109 int processNum = 1; 110 for (ExampleScenarioProcessComponent process: scen.getProcess()) { 111 plantUml += toPlantUml(status, res, process, Integer.toString(processNum), scen, actorKeys); 112 processNum++; 113 } 114 plantUml += "@enduml"; 115 116 return plantUml; 117 } 118 119 private String escapeKey(String origKey) { 120 char[] chars = origKey.toCharArray(); 121 for (int i=0; i<chars.length; i++) { 122 char c = chars[i]; 123 if (!((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9') || c=='@' || c=='.')) 124 chars[i] = '_'; 125 } 126 return new String(chars); 127 } 128 129 protected String toPlantUml(RenderingStatus status, ResourceWrapper res, ExampleScenarioProcessComponent process, String prefix, ExampleScenario scen, Map<String, String> actorKeys) throws IOException { 130 String plantUml = "group " + process.getTitle() + " " + creolLink("details", "#p_" + prefix, process.getDescription()) + "\r\n"; 131 132 Map<String,Boolean> actorsActive = new HashMap<String, Boolean>(); 133 for (ExampleScenarioActorComponent actor : scen.getActor()) { 134 actorsActive.put(actor.getKey(), Boolean.FALSE); 135 } 136 int stepCount = 1; 137 for (ExampleScenarioProcessStepComponent step: process.getStep()) { 138 plantUml += toPlantUml(status, res, step, stepPrefix(prefix, step, stepCount), scen, actorsActive, actorKeys); 139 if (step.getPause()) 140 plantUml += context.formatPhrase(RenderingContext.EX_SCEN_TIME)+"\n"; 141 stepCount++; 142 } 143 144 plantUml += "end\r\n\r\n"; 145 return plantUml; 146 } 147 148 protected String toPlantUml(RenderingStatus status, ResourceWrapper res, ExampleScenarioProcessStepComponent step, String prefix, ExampleScenario scen, Map<String,Boolean> actorsActive, Map<String, String> actorKeys) throws IOException { 149 String plantUml = ""; 150 if (step.hasWorkflow()) { 151 XhtmlNode n = new XhtmlDocument(); 152 renderCanonical(status, res, n, Resource.class, step.getWorkflowElement()); 153 XhtmlNode ref = n.getChildNodes().get(0); 154 plantUml += noteOver(scen.getActor(), context.formatPhrase(RenderingContext.EXAMPLE_SCEN_STEP_SCEN, trimPrefix(prefix), creolLink((ref.getContent()), ref.getAttribute("href")))); 155 } else if (step.hasProcess()) 156 plantUml += toPlantUml(status, res, step.getProcess(), prefix, scen, actorKeys); 157 else { 158 // Operation 159 plantUml += toPlantUml(step.getOperation(), prefix, scen, actorsActive, actorKeys); 160 } 161 162 return plantUml; 163 } 164 165 protected String toPlantUml(ExampleScenarioProcessStepOperationComponent op, String prefix, ExampleScenario scen, Map<String,Boolean> actorsActive, Map<String, String> actorKeys) { 166 StringBuilder plantUml = new StringBuilder(); 167 plantUml.append(handleActivation(op.getInitiator(), op.getInitiatorActive(), actorsActive, actorKeys)); 168 plantUml.append(handleActivation(op.getReceiver(), op.getReceiverActive(), actorsActive, actorKeys)); 169 plantUml.append(actorKeys.get(op.getInitiator()) + " -> " + actorKeys.get(op.getReceiver()) + ": "); 170 plantUml.append(creolLink(op.getTitle(), "#s_" + prefix, op.getDescription())); 171 if (op.hasRequest()) { 172 plantUml.append(" (" + creolLink("payload", linkForInstance(op.getRequest())) + ")\r\n"); 173 } 174 if (op.hasResponse()) { 175 plantUml.append("activate " + actorKeys.get(op.getReceiver()) + "\r\n"); 176 plantUml.append(actorKeys.get(op.getReceiver()) + " --> " + actorKeys.get(op.getInitiator()) + ": "); 177 plantUml.append(creolLink("response", "#s_" + prefix, op.getDescription())); 178 plantUml.append(" (" + creolLink("payload", linkForInstance(op.getResponse())) + ")\r\n"); 179 plantUml.append("deactivate " + actorKeys.get(op.getReceiver()) + "\r\n"); 180 } 181 plantUml.append(handleDeactivation(op.getInitiator(), op.getInitiatorActive(), actorsActive, actorKeys)); 182 plantUml.append(handleDeactivation(op.getReceiver(), op.getReceiverActive(), actorsActive, actorKeys)); 183 184 return plantUml.toString(); 185 } 186 187 private String handleActivation(String actorId, boolean active, Map<String,Boolean> actorsActive, Map<String, String> actorKeys) { 188 String plantUml = ""; 189 Boolean actorWasActive = actorsActive.get(actorId); 190 if (active && !actorWasActive) { 191 plantUml += "activate " + actorKeys.get(actorId) + "\r\n"; 192 } 193 return plantUml; 194 } 195 196 private String handleDeactivation(String actorId, boolean active, Map<String,Boolean> actorsActive, Map<String, String> actorKeys) { 197 String plantUml = ""; 198 Boolean actorWasActive = actorsActive.get(actorId); 199 if (!active && actorWasActive) { 200 plantUml += "deactivate " + actorKeys.get(actorId) + "\r\n"; 201 } 202 if (active != actorWasActive) { 203 actorsActive.remove(actorId); 204 actorsActive.put(actorId, Boolean.valueOf(active)); 205 } 206 return plantUml; 207 } 208 209 private String linkForInstance(ExampleScenarioInstanceContainedInstanceComponent ref) { 210 String plantUml = "#i_" + ref.getInstanceReference(); 211 if (ref.hasVersionReference()) 212 plantUml += "v_" + ref.getVersionReference(); 213 return plantUml; 214 } 215 216 private String trimPrefix(String prefix){ 217 return prefix.substring(prefix.lastIndexOf(".") + 1); 218 } 219 220 private String noteOver(List<ExampleScenarioActorComponent> actors, String text) { 221 String plantUml = "Note over "; 222 List actorKeys = new ArrayList<String>(); 223 for (ExampleScenarioActorComponent actor: actors) { 224 actorKeys.add(actor.getKey()); 225 } 226 plantUml += String.join(", ", actorKeys); 227 plantUml += " " + text; 228 return plantUml; 229 } 230 231 private String creolLink(String text, String url) { 232 return creolLink(text, url, null); 233 } 234 235 private String creolLink(String text, String url, String flyover) { 236 String s = "[[" + url; 237 if (flyover!=null) 238 s += "{" + flyover + "}"; 239 s += " " + text + "]]"; 240 return s; 241 } 242 243 public boolean renderActors(RenderingStatus status, ResourceWrapper res, XhtmlNode x, ExampleScenario scen) throws IOException { 244 XhtmlNode tbl = x.table("table-striped table-bordered"); 245 XhtmlNode thead = tbl.tr(); 246 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_NAME)); 247 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_TYPE)); 248 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_DESC)); 249 for (ExampleScenarioActorComponent actor : scen.getActor()) { 250 XhtmlNode tr = tbl.tr(); 251 XhtmlNode nameCell = tr.td(); 252 nameCell.an(context.prefixAnchor("a_" + actor.getKey())); 253 nameCell.tx(actor.getTitle()); 254 tr.td().tx(actor.getType().getDisplay()); 255 addMarkdown(tr.td().style("overflow-wrap:break-word"), actor.getDescription()); 256 } 257 return true; 258 } 259 260 public boolean renderInstances(RenderingStatus status, ResourceWrapper res, XhtmlNode x, ExampleScenario scen) throws IOException { 261 XhtmlNode tbl = x.table("table-striped table-bordered"); 262 XhtmlNode thead = tbl.tr(); 263 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_NAME)); 264 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_TYPE)); 265 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_CONTENT)); 266 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_DESC)); 267 268 Map<String, String> instanceNames = new HashMap<String, String>(); 269 for (ExampleScenarioInstanceComponent instance : scen.getInstance()) { 270 instanceNames.put("i_" + instance.getKey(), instance.getTitle()); 271 if (instance.hasVersion()) { 272 for (ExampleScenarioInstanceVersionComponent version: instance.getVersion()) { 273 instanceNames.put("i_" + instance.getKey() + "v_" + version.getKey(), version.getTitle()); 274 } 275 } 276 } 277 278 for (ExampleScenarioInstanceComponent instance : scen.getInstance()) { 279 XhtmlNode row = tbl.tr(); 280 XhtmlNode nameCell = row.td(); 281 nameCell.an(context.prefixAnchor("i_" + instance.getKey())); 282 nameCell.tx(instance.getTitle()); 283 XhtmlNode typeCell = row.td(); 284 if (instance.hasVersion()) 285 typeCell.attribute("rowSpan", Integer.toString(instance.getVersion().size()+1)); 286 287 if (!instance.hasStructureVersion() || instance.getStructureType().getSystem().equals("")) { 288 if (instance.hasStructureVersion()) 289 typeCell.tx((context.formatPhrase(RenderingContext.EX_SCEN_FVER, instance.getStructureVersion()) + " ") + " "); 290 if (instance.hasStructureProfileCanonicalType()) { 291 renderCanonical(status, res, typeCell, StructureDefinition.class, instance.getStructureProfileCanonicalType()); 292 } else if (instance.hasStructureProfileUriType()) { 293 renderBase(status, typeCell, instance.getStructureProfileUriType()); 294 } else { 295 CanonicalType ct = new CanonicalType("http://hl7.org/fhir/StructureDefinition/" + instance.getStructureType().getCode()); 296 renderCanonical(status, res, typeCell, StructureDefinition.class, ct); 297 } 298 } else { 299 renderDataType(status, typeCell, wrapWC(res, instance.getStructureVersionElement())); 300 typeCell.tx(" "+(context.formatPhrase(RenderingContext.GENERAL_VER_LOW, instance.getStructureVersion())+" ")); 301 if (instance.hasStructureProfile()) { 302 typeCell.tx(" "); 303 if (instance.hasStructureProfileCanonicalType()) { 304 renderCanonical(status, res, typeCell, StructureDefinition.class, instance.getStructureProfileCanonicalType()); 305 } 306 } 307 } 308 if (instance.hasContent() && instance.getContent().hasReference()) { 309 // Force end-user mode to avoid ugly references 310 RenderingContext.ResourceRendererMode mode = context.getMode(); 311 context.setMode(RenderingContext.ResourceRendererMode.END_USER); 312 renderReference(status, row.td(), wrapWC(res, instance.getContent().copy().setDisplay("here"))); 313 context.setMode(mode); 314 } else 315 row.td(); 316 317 XhtmlNode descCell = row.td(); 318 addMarkdown(descCell, instance.getDescription()); 319 if (instance.hasContainedInstance()) { 320 descCell.b().tx(context.formatPhrase(RenderingContext.EX_SCEN_CONTA) + " "); 321 int containedCount = 1; 322 for (ExampleScenarioInstanceContainedInstanceComponent contained: instance.getContainedInstance()) { 323 String key = "i_" + contained.getInstanceReference(); 324 if (contained.hasVersionReference()) 325 key += "v_" + contained.getVersionReference(); 326 String description = instanceNames.get(key); 327 if (description==null) 328 throw new FHIRException("Unable to find contained instance " + key + " under " + instance.getKey()); 329 descCell.ah(context.prefixLocalHref("#" + key)).tx(description); 330 containedCount++; 331 if (instance.getContainedInstance().size() > containedCount) 332 descCell.tx(", "); 333 } 334 } 335 336 for (ExampleScenarioInstanceVersionComponent version: instance.getVersion()) { 337 row = tbl.tr(); 338 nameCell = row.td().style("padding-left: 10px;"); 339 nameCell.an("i_" + instance.getKey() + "v_" + version.getKey()); 340 XhtmlNode nameItem = nameCell.ul().li(); 341 nameItem.tx(version.getTitle()); 342 343 if (version.hasContent() && version.getContent().hasReference()) { 344 // Force end-user mode to avoid ugly references 345 RenderingContext.ResourceRendererMode mode = context.getMode(); 346 context.setMode(RenderingContext.ResourceRendererMode.END_USER); 347 renderReference(status, row.td(), wrapWC(res, version.getContent().copy().setDisplay("here"))); 348 context.setMode(mode); 349 } else 350 row.td(); 351 352 descCell = row.td(); 353 addMarkdown(descCell, instance.getDescription()); 354 } 355 } 356 return true; 357 } 358 359 public boolean renderProcesses(RenderingStatus status, XhtmlNode x, ExampleScenario scen) throws IOException { 360 Map<String, ExampleScenarioActorComponent> actors = new HashMap<>(); 361 for (ExampleScenarioActorComponent actor: scen.getActor()) { 362 actors.put(actor.getKey(), actor); 363 } 364 365 Map<String, ExampleScenarioInstanceComponent> instances = new HashMap<>(); 366 for (ExampleScenarioInstanceComponent instance: scen.getInstance()) { 367 instances.put(instance.getKey(), instance); 368 } 369 370 int num = 1; 371 for (ExampleScenarioProcessComponent process : scen.getProcess()) { 372 renderProcess(status, x, process, Integer.toString(num), actors, instances); 373 num++; 374 } 375 return true; 376 } 377 378 public void renderProcess(RenderingStatus status, XhtmlNode x, ExampleScenarioProcessComponent process, String prefix, Map<String, ExampleScenarioActorComponent> actors, Map<String, ExampleScenarioInstanceComponent> instances) throws IOException { 379 XhtmlNode div = x.div(); 380 div.an(context.prefixAnchor("p_" + prefix)); 381 div.b().tx(context.formatPhrase(RenderingContext.EX_SCEN_PROC, process.getTitle())+" "); 382 if (process.hasDescription()) 383 addMarkdown(div, process.getDescription()); 384 if (process.hasPreConditions()) { 385 div.para().b().i().tx(context.formatPhrase(RenderingContext.EX_SCEN_PRECON)); 386 addMarkdown(div, process.getPreConditions()); 387 } 388 if (process.hasPostConditions()) { 389 div.para().b().i().tx(context.formatPhrase(RenderingContext.EX_SCEN_POSTCON)); 390 addMarkdown(div, process.getPostConditions()); 391 } 392 XhtmlNode tbl = div.table("table-striped table-bordered").style("width:100%"); 393 XhtmlNode thead = tbl.tr(); 394 thead.th().addText(context.formatPhrase(RenderingContext.EX_SCEN_STEP)); 395 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_NAME)); 396 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_DESC)); 397 thead.th().addText(context.formatPhrase(RenderingContext.EX_SCEN_IN)); 398 thead.th().addText(context.formatPhrase(RenderingContext.EX_SCEN_REC)); 399 thead.th().addText(context.formatPhrase(RenderingContext.GENERAL_REQUEST)); 400 thead.th().addText(context.formatPhrase(RenderingContext.EX_SCEN_RES)); 401 int stepCount = 1; 402 for (ExampleScenarioProcessStepComponent step: process.getStep()) { 403 renderStep(status, tbl, step, stepPrefix(prefix, step, stepCount), actors, instances); 404 stepCount++; 405 } 406 407 // Now go through the steps again and spit out any child processes 408 stepCount = 1; 409 for (ExampleScenarioProcessStepComponent step: process.getStep()) { 410 stepSubProcesses(status, tbl, step, stepPrefix(prefix, step, stepCount), actors, instances); 411 stepCount++; 412 } 413 } 414 415 private String stepPrefix(String prefix, ExampleScenarioProcessStepComponent step, int stepCount) { 416 String num = step.hasNumber() ? step.getNumber() : Integer.toString(stepCount); 417 return (!prefix.isEmpty() ? prefix + "." : "") + num; 418 } 419 420 private String altStepPrefix(String prefix, ExampleScenarioProcessStepComponent step, int altNum, int stepCount) { 421 return stepPrefix(prefix + "-Alt" + Integer.toString(altNum) + ".", step, stepCount); 422 } 423 424 private void stepSubProcesses(RenderingStatus status, XhtmlNode x, ExampleScenarioProcessStepComponent step, String prefix, Map<String, ExampleScenarioActorComponent> actors, Map<String, ExampleScenarioInstanceComponent> instances) throws IOException { 425 if (step.hasProcess()) 426 renderProcess(status, x, step.getProcess(), prefix, actors, instances); 427 if (step.hasAlternative()) { 428 int altNum = 1; 429 for (ExampleScenarioProcessStepAlternativeComponent alt: step.getAlternative()) { 430 int stepCount = 1; 431 for (ExampleScenarioProcessStepComponent altStep: alt.getStep()) { 432 stepSubProcesses(status, x, altStep, altStepPrefix(prefix, altStep, altNum, stepCount), actors, instances); 433 stepCount++; 434 } 435 altNum++; 436 } 437 } 438 } 439 440 private boolean renderStep(RenderingStatus status, XhtmlNode tbl, ExampleScenarioProcessStepComponent step, String stepLabel, Map<String, ExampleScenarioActorComponent> actors, Map<String, ExampleScenarioInstanceComponent> instances) throws IOException { 441 XhtmlNode row = tbl.tr(); 442 XhtmlNode prefixCell = row.td(); 443 prefixCell.an(context.prefixAnchor("s_" + stepLabel)); 444 prefixCell.tx(stepLabel.substring(stepLabel.indexOf(".") + 1)); 445 if (step.hasProcess()) { 446 XhtmlNode n = row.td().colspan(6); 447 n.tx(context.formatPhrase(RenderingContext.EX_SCEN_SEE)); 448 n.ah(context.prefixLocalHref("#p_" + stepLabel), step.getProcess().getTitle()); 449 n.tx(" "+ context.formatPhrase(RenderingContext.EX_SCEN_BEL)); 450 451 } else if (step.hasWorkflow()) { 452 XhtmlNode n = row.td().colspan(6); 453 n.tx(context.formatPhrase(RenderingContext.EX_SCEN_OTH)); 454 String link = new ContextUtilities(context.getWorker()).getLinkForUrl(context.getLink(KnownLinkType.SPEC), step.getWorkflow()); 455 n.ah(context.prefixLocalHref(link), step.getProcess().getTitle()); 456 457 } else { 458 // Must be an operation 459 ExampleScenarioProcessStepOperationComponent op = step.getOperation(); 460 XhtmlNode name = row.td(); 461 name.tx(op.getTitle()); 462 if (op.hasType()) { 463 name.tx(" - "); 464 renderCoding(status, name, wrapNC(op.getType())); 465 } 466 XhtmlNode descCell = row.td(); 467 addMarkdown(descCell, op.getDescription()); 468 469 addActor(row, op.getInitiator(), actors); 470 addActor(row, op.getReceiver(), actors); 471 addInstance(row, op.getRequest(), instances); 472 addInstance(row, op.getResponse(), instances); 473 } 474 475 int altNum = 1; 476 for (ExampleScenarioProcessStepAlternativeComponent alt : step.getAlternative()) { 477 XhtmlNode altHeading = tbl.tr().colspan(7).td(); 478 altHeading.para().i().tx(context.formatPhrase(RenderingContext.EX_SCEN_ALT, alt.getTitle())+" "); 479 if (alt.hasDescription()) 480 addMarkdown(altHeading, alt.getDescription()); 481 int stepCount = 1; 482 for (ExampleScenarioProcessStepComponent subStep : alt.getStep()) { 483 renderStep(status, tbl, subStep, altStepPrefix(stepLabel, step, altNum, stepCount), actors, instances); 484 stepCount++; 485 } 486 altNum++; 487 } 488 489 return true; 490 } 491 492 private void addActor(XhtmlNode row, String actorId, Map<String, ExampleScenarioActorComponent> actors) throws FHIRException { 493 XhtmlNode actorCell = row.td(); 494 if (actorId==null) 495 return; 496 ExampleScenarioActorComponent actor = actors.get(actorId); 497 if (actor==null) 498 throw new FHIRException(context.formatPhrase(RenderingContext.EX_SCEN_UN_ACT, actorId)+" "); 499 actorCell.ah("#a_" + actor.getKey(), actor.getDescription()).tx(actor.getTitle()); 500 } 501 502 private void addInstance(XhtmlNode row, ExampleScenarioInstanceContainedInstanceComponent instanceRef, Map<String, ExampleScenarioInstanceComponent> instances) { 503 XhtmlNode instanceCell = row.td(); 504 if (instanceRef==null || instanceRef.getInstanceReference()==null) 505 return; 506 ExampleScenarioInstanceComponent instance = instances.get(instanceRef.getInstanceReference()); 507 if (instance==null) 508 throw new FHIRException(context.formatPhrase(RenderingContext.EX_SCEN_UN_INST, instanceRef.getInstanceReference())+" "); 509 if (instanceRef.hasVersionReference()) { 510 ExampleScenarioInstanceVersionComponent theVersion = null; 511 for (ExampleScenarioInstanceVersionComponent version: instance.getVersion()) { 512 if (version.getKey().equals(instanceRef.getVersionReference())) { 513 theVersion = version; 514 break; 515 } 516 } 517 if (theVersion==null) 518 throw new FHIRException("Unable to find referenced version " + instanceRef.getVersionReference() + " within instance " + instanceRef.getInstanceReference()); 519 instanceCell.ah(context.prefixLocalHref("#i_" + instance.getKey() + "v_"+ theVersion.getKey()) , theVersion.getDescription()).tx(theVersion.getTitle()); 520 521 } else 522 instanceCell.ah(context.prefixLocalHref("#i_" + instance.getKey()), instance.getDescription()).tx(instance.getTitle()); 523 } 524}