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