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