
001package org.hl7.fhir.r5.renderers; 002 003import java.io.IOException; 004import java.util.ArrayList; 005import java.util.HashMap; 006import java.util.List; 007 008import org.hl7.fhir.exceptions.DefinitionException; 009import org.hl7.fhir.exceptions.FHIRFormatError; 010import org.hl7.fhir.r5.model.ActorDefinition; 011import org.hl7.fhir.r5.model.CanonicalType; 012import org.hl7.fhir.r5.model.Coding; 013import org.hl7.fhir.r5.model.ElementDefinition; 014import org.hl7.fhir.r5.model.Extension; 015import org.hl7.fhir.r5.model.StructureDefinition; 016import org.hl7.fhir.r5.model.UsageContext; 017import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution; 018import org.hl7.fhir.r5.renderers.utils.RenderingContext; 019import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 020import org.hl7.fhir.r5.utils.ToolingExtensions; 021import org.hl7.fhir.r5.utils.UserDataNames; 022import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 023import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; 024import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; 025import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; 026import org.hl7.fhir.utilities.xhtml.NodeType; 027import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 028import org.hl7.fhir.utilities.xhtml.XhtmlNode; 029import org.hl7.fhir.utilities.xhtml.XhtmlNodeList; 030 031@MarkedToMoveToAdjunctPackage 032public class ObligationsRenderer extends Renderer { 033 public static class ObligationDetail { 034 private List<String> codes = new ArrayList<>(); 035 private List<String> elementIds = new ArrayList<>(); 036 private List<CanonicalType> actors = new ArrayList<>(); 037 private String doco; 038 private String docoShort; 039 private String filter; 040 private String filterDoco; 041 private List<UsageContext> usage = new ArrayList<>(); 042 private boolean isUnchanged = false; 043 private boolean matched = false; 044 private boolean removed = false; 045 private String source; 046// private ValueSet vs; 047 048 private ObligationDetail compare; 049 private int count = 1; 050 051 public ObligationDetail(Extension ext) { 052 for (Extension e: ext.getExtensionsByUrl("code")) { 053 codes.add(e.getValueStringType().toString()); 054 } 055 for (Extension e: ext.getExtensionsByUrl("actor")) { 056 actors.add(e.getValueCanonicalType()); 057 } 058 this.doco = ext.getExtensionString("documentation"); 059 this.docoShort = ext.getExtensionString("shortDoco"); 060 this.filter = ext.getExtensionString("filter"); 061 this.filterDoco = ext.getExtensionString("filterDocumentation"); 062 if (this.filterDoco == null) { 063 this.filterDoco = ext.getExtensionString("filter-desc"); 064 } 065 for (Extension usage : ext.getExtensionsByUrl("usage")) { 066 this.usage.add(usage.getValueUsageContext()); 067 } 068 for (Extension eid : ext.getExtensionsByUrl("elementId")) { 069 this.elementIds.add(eid.getValue().primitiveValue()); 070 } 071 this.isUnchanged = ext.hasUserData(UserDataNames.SNAPSHOT_DERIVATION_EQUALS); 072 if (ext.hasExtension(ToolingExtensions.EXT_OBLIGATION_SOURCE, ToolingExtensions.EXT_OBLIGATION_SOURCE_SHORT)) { 073 this.source = ext.getExtensionString(ToolingExtensions.EXT_OBLIGATION_SOURCE, ToolingExtensions.EXT_OBLIGATION_SOURCE_SHORT); 074 } else if (ext.hasUserData(UserDataNames.SNAPSHOT_EXTENSION_SOURCE)) { 075 this.source = ((StructureDefinition) ext.getUserData(UserDataNames.SNAPSHOT_EXTENSION_SOURCE)).getVersionedUrl(); 076 } 077 } 078 079 private String getKey() { 080 // Todo: Consider extending this with content from usageContext if purpose isn't sufficiently differentiating 081 return String.join(",", codes) + Integer.toString(count); 082 } 083 084 private void incrementCount() { 085 count++; 086 } 087 private void setCompare(ObligationDetail match) { 088 compare = match; 089 match.matched = true; 090 } 091 private boolean alreadyMatched() { 092 return matched; 093 } 094 public String getDoco(boolean full) { 095 return full ? doco : docoShort; 096 } 097 public String getCodes() { 098 return String.join(",", codes); 099 } 100 public List<String> getCodeList() { 101 return new ArrayList<String>(codes); 102 } 103 public boolean unchanged() { 104 if (!isUnchanged) 105 return false; 106 if (compare==null) 107 return true; 108 isUnchanged = true; 109 isUnchanged = isUnchanged && ((codes.isEmpty() && compare.codes.isEmpty()) || codes.equals(compare.codes)); 110 isUnchanged = elementIds.equals(compare.elementIds); 111 isUnchanged = isUnchanged && ((actors.isEmpty() && compare.actors.isEmpty()) || actors.equals(compare.actors)); 112 isUnchanged = isUnchanged && ((doco==null && compare.doco==null) || doco.equals(compare.doco)); 113 isUnchanged = isUnchanged && ((docoShort==null && compare.docoShort==null) || docoShort.equals(compare.docoShort)); 114 isUnchanged = isUnchanged && ((filter==null && compare.filter==null) || filter.equals(compare.filter)); 115 isUnchanged = isUnchanged && ((filterDoco==null && compare.filterDoco==null) || filterDoco.equals(compare.filterDoco)); 116 isUnchanged = isUnchanged && ((usage==null && compare.usage==null) || usage.equals(compare.usage)); 117 return isUnchanged; 118 } 119 120 public boolean hasFilter() { 121 return filter != null; 122 } 123 124 public boolean hasUsage() { 125 return !usage.isEmpty(); 126 } 127 128 public String getFilterDesc() { 129 return filterDoco; 130 } 131 132 public String getFilter() { 133 return filter; 134 } 135 136 public List<UsageContext> getUsage() { 137 return usage; 138 } 139 140 public boolean hasActors() { 141 return !actors.isEmpty(); 142 } 143 144 public boolean hasActor(String id) { 145 for (CanonicalType actor: actors) { 146 if (actor.getValue().equals(id)) 147 return true; 148 } 149 return false; 150 } 151 } 152 153 private static String STYLE_UNCHANGED = "opacity: 0.5;"; 154 private static String STYLE_REMOVED = STYLE_UNCHANGED + "text-decoration: line-through;"; 155 156 private List<ObligationDetail> obligations = new ArrayList<>(); 157 private String corePath; 158 private StructureDefinition profile; 159 private String path; 160 private RenderingContext context; 161 private IMarkdownProcessor md; 162 private CodeResolver cr; 163 private boolean canDoNoList; 164 165 public ObligationsRenderer(String corePath, StructureDefinition profile, String path, RenderingContext context, IMarkdownProcessor md, CodeResolver cr, boolean canDoNoList) { 166 super(context); 167 this.corePath = corePath; 168 this.profile = profile; 169 this.path = path; 170 this.context = context; 171 this.md = md; 172 this.cr = cr; 173 this.canDoNoList = canDoNoList; 174 } 175 176 177 public void seeObligations(ElementDefinition element, String id) { 178 seeObligations(element.getExtension(), null, false, id); 179 } 180 181 public void seeObligations(List<Extension> list) { 182 seeObligations(list, null, false, "$all"); 183 } 184 185 public void seeRootObligations(String eid, List<Extension> list) { 186 seeRootObligations(eid, list, null, false, "$all"); 187 } 188 189 public void seeObligations(List<Extension> list, List<Extension> compList, boolean compare, String id) { 190 HashMap<String, ObligationDetail> compBindings = new HashMap<String, ObligationDetail>(); 191 if (compare && compList!=null) { 192 for (Extension ext : compList) { 193 ObligationDetail abr = obligationDetail(ext); 194 if (compBindings.containsKey(abr.getKey())) { 195 abr.incrementCount(); 196 } 197 compBindings.put(abr.getKey(), abr); 198 } 199 } 200 201 for (Extension ext : list) { 202 ObligationDetail obd = obligationDetail(ext); 203 if ("$all".equals(id) || (obd.hasActor(id))) { 204 if (compare && compList!=null) { 205 ObligationDetail match = null; 206 do { 207 match = compBindings.get(obd.getKey()); 208 if (obd.alreadyMatched()) 209 obd.incrementCount(); 210 } while (match!=null && obd.alreadyMatched()); 211 if (match!=null) 212 obd.setCompare(match); 213 addObligation(obd); 214 if (obd.compare!=null) 215 compBindings.remove(obd.compare.getKey()); 216 } else { 217 addObligation(obd); 218 } 219 } 220 } 221 for (ObligationDetail b: compBindings.values()) { 222 b.removed = true; 223 addObligation(b); 224 } 225 } 226 227 public void seeRootObligations(String eid, List<Extension> list, List<Extension> compList, boolean compare, String id) { 228 HashMap<String, ObligationDetail> compBindings = new HashMap<String, ObligationDetail>(); 229 if (compare && compList!=null) { 230 for (Extension ext : compList) { 231 if (forElement(eid, ext)) { 232 ObligationDetail abr = obligationDetail(ext); 233 if (compBindings.containsKey(abr.getKey())) { 234 abr.incrementCount(); 235 } 236 compBindings.put(abr.getKey(), abr); 237 } 238 } 239 } 240 241 for (Extension ext : list) { 242 if (forElement(eid, ext)) { 243 ObligationDetail obd = obligationDetail(ext); 244 obd.elementIds.clear(); 245 if ("$all".equals(id) || (obd.hasActor(id))) { 246 if (compare && compList!=null) { 247 ObligationDetail match = null; 248 do { 249 match = compBindings.get(obd.getKey()); 250 if (obd.alreadyMatched()) 251 obd.incrementCount(); 252 } while (match!=null && obd.alreadyMatched()); 253 if (match!=null) 254 obd.setCompare(match); 255 addObligation(obd); 256 if (obd.compare!=null) 257 compBindings.remove(obd.compare.getKey()); 258 } else { 259 addObligation(obd); 260 } 261 } 262 } 263 } 264 for (ObligationDetail b: compBindings.values()) { 265 b.removed = true; 266 addObligation(b); 267 } 268 } 269 270 271 private void addObligation(ObligationDetail obd) { 272 boolean add = context.getActorWhiteList().isEmpty(); 273 if (!add) { 274 for (CanonicalType a : obd.actors) { 275 ActorDefinition ad = context.getContext().fetchResource(ActorDefinition.class, a.getValue()); 276 add = add || (context.getActorWhiteList().contains(ad)); 277 } 278 } 279 if (add) { 280 obligations.add(obd); 281 } 282 } 283 284 285 private boolean forElement(String eid, Extension ext) { 286 287 for (Extension exid : ext.getExtensionsByUrl("elementId")) { 288 if (eid.equals(exid.getValue().primitiveValue())) { 289 return true; 290 } 291 } 292 return false; 293 } 294 295 296 protected ObligationDetail obligationDetail(Extension ext) { 297 ObligationDetail abr = new ObligationDetail(ext); 298 return abr; 299 } 300 301 public String render(RenderingStatus status, ResourceWrapper res, String defPath, String anchorPrefix, List<ElementDefinition> inScopeElements) throws IOException { 302 if (obligations.isEmpty()) { 303 return ""; 304 } else { 305 XhtmlNode tbl = new XhtmlNode(NodeType.Element, "table"); 306 tbl.attribute("class", "grid"); 307 renderTable(status, res, tbl.getChildNodes(), true, defPath, anchorPrefix, inScopeElements); 308 return new XhtmlComposer(false).compose(tbl); 309 } 310 } 311 312 public void renderTable(RenderingStatus status, ResourceWrapper res, HierarchicalTableGenerator gen, Cell c, List<ElementDefinition> inScopeElements) throws FHIRFormatError, DefinitionException, IOException { 313 if (obligations.isEmpty()) { 314 return; 315 } else { 316 Piece piece = gen.new Piece("obligation", "table").setClass("grid"); 317 c.getPieces().add(piece); 318 renderTable(status, res, piece.getChildren(), false, gen.getDefPath(), gen.getUniqueLocalPrefix(), inScopeElements); 319 } 320 } 321 322 public void renderList(HierarchicalTableGenerator gen, Cell c) throws FHIRFormatError, DefinitionException, IOException { 323 if (obligations.size() > 0) { 324 Piece p = gen.new Piece(null); 325 c.addPiece(p); 326 if (obligations.size() == 1 && canDoNoList) { 327 renderObligationLI(p.getChildren(), obligations.get(0)); 328 } else { 329 XhtmlNode ul = p.getChildren().ul(); 330 for (ObligationDetail ob : obligations) { 331 renderObligationLI(ul.li().getChildNodes(), ob); 332 } 333 } 334 } 335 } 336 337 private void renderObligationLI(XhtmlNodeList children, ObligationDetail ob) throws IOException { 338 renderCodes(children, ob.getCodeList()); 339 if (ob.hasFilter() || ob.hasUsage() || !ob.elementIds.isEmpty()) { 340 children.tx(" ("); 341 boolean ffirst = !ob.hasFilter(); 342 boolean firstEid = true; 343 344 for (String eid: ob.elementIds) { 345 if (firstEid) { 346 children.span().i().tx("Elements: "); 347 firstEid = false; 348 } else 349 children.tx(", "); 350 String trimmedElement = eid.substring(eid.indexOf(".")+ 1); 351 children.tx(trimmedElement); 352 } 353 if (ob.hasFilter()) { 354 children.span(null, ob.getFilterDesc()).code().tx(ob.getFilter()); 355 } 356 for (UsageContext uc : ob.getUsage()) { 357 if (ffirst) ffirst = false; else children.tx(","); 358 if (!uc.getCode().is("http://terminology.hl7.org/CodeSystem/usage-context-type", "jurisdiction")) { 359 children.tx(displayForUsage(uc.getCode())); 360 children.tx("="); 361 } 362 CodeResolution ccr = this.cr.resolveCode(uc.getValueCodeableConcept()); 363 children.ah(context.prefixLocalHref(ccr.getLink()), ccr.getHint()).tx(ccr.getDisplay()); 364 } 365 children.tx(")"); 366 } 367 if (ob.source != null && !ob.source.equals(profile.getVersionedUrl())) { 368 children.tx(" "); 369 StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, ob.source); 370 String link = sd != null ? sd.getWebPath() : ob.source; 371 String title = context.formatPhrase(RenderingContext.OBLIGATION_SOURCE, sd == null ? ob.source : sd.present()); 372 children.ah(link, title).attribute("data-no-external", "true").img("external.png", "source-link"); 373 } 374 // usage 375 // filter 376 // process 377 } 378 379 380 public void renderTable(RenderingStatus status, ResourceWrapper res, List<XhtmlNode> children, boolean fullDoco, String defPath, String anchorPrefix, List<ElementDefinition> inScopeElements) throws FHIRFormatError, DefinitionException, IOException { 381 boolean hasDoco = false; 382 boolean hasUsage = false; 383 boolean hasActor = false; 384 boolean hasFilter = false; 385 boolean hasElementId = false; 386 boolean hasSource = false; 387 for (ObligationDetail ob : obligations) { 388 hasActor = hasActor || !ob.actors.isEmpty() || (ob.compare!=null && !ob.compare.actors.isEmpty()); 389 hasDoco = hasDoco || ob.getDoco(fullDoco)!=null || (ob.compare!=null && ob.compare.getDoco(fullDoco)!=null); 390 hasUsage = hasUsage || !ob.usage.isEmpty() || (ob.compare!=null && !ob.compare.usage.isEmpty()); 391 hasFilter = hasFilter || ob.filter != null || (ob.compare!=null && ob.compare.filter!=null); 392 hasElementId = hasElementId || !ob.elementIds.isEmpty() || (ob.compare!=null && !ob.compare.elementIds.isEmpty()); 393 hasSource = hasSource || ((ob.source != null || (ob.compare!=null && ob.compare.source!=null)) && !ob.source.equals(profile.getVersionedUrl())); 394 } 395 396 List<String> inScopePaths = new ArrayList<>(); 397 for (ElementDefinition e: inScopeElements) { 398 inScopePaths.add(e.getPath()); 399 } 400 401 XhtmlNode tr = new XhtmlNode(NodeType.Element, "tr"); 402 children.add(tr); 403 tr.td().style("font-size: 11px").b().tx(context.formatPhrase(RenderingContext.GENERAL_OBLIG)); 404 if (hasActor) { 405 tr.td().style("font-size: 11px").b().tx(context.formatPhrase(RenderingContext.OBLIG_ACT)); 406 } 407 if (hasElementId) { 408 tr.td().style("font-size: 11px").b().tx(context.formatPhrase(RenderingContext.OBLIG_ELE)); 409 } 410 if (hasUsage) { 411 tr.td().style("font-size: 11px").b().tx(context.formatPhrase(RenderingContext.GENERAL_USAGE)); 412 } 413 if (hasDoco) { 414 tr.td().style("font-size: 11px").b().tx(context.formatPhrase(RenderingContext.GENERAL_DOCUMENTATION)); 415 } 416 if (hasFilter) { 417 tr.td().style("font-size: 11px").b().tx(context.formatPhrase(RenderingContext.GENERAL_FILTER)); 418 } 419 if (hasSource) { 420 tr.td().style("font-size: 11px").b().tx(context.formatPhrase(RenderingContext.GENERAL_SOURCE)); 421 } 422 for (ObligationDetail ob : obligations) { 423 tr = new XhtmlNode(NodeType.Element, "tr"); 424 if (ob.unchanged()) { 425 tr.style(STYLE_REMOVED); 426 } else if (ob.removed) { 427 tr.style(STYLE_REMOVED); 428 } 429 children.add(tr); 430 431 XhtmlNode code = tr.td().style("font-size: 11px"); 432 if (ob.compare!=null && ob.getCodes().equals(ob.compare.getCodes())) 433 code.style("font-color: darkgray"); 434 renderCodes(code.getChildNodes(), ob.getCodeList()); 435 if (ob.compare!=null && !ob.compare.getCodeList().isEmpty() && !ob.getCodes().equals(ob.compare.getCodes())) { 436 code.br(); 437 code = code.span(STYLE_UNCHANGED, null); 438 renderCodes(code.getChildNodes(), ob.compare.getCodeList()); 439 } 440 441 XhtmlNode actorId = tr.td().style("font-size: 11px"); 442 if (!ob.actors.isEmpty() || ob.compare.actors.isEmpty()) { 443 boolean firstActor = true; 444 for (CanonicalType anActor : ob.actors) { 445 ActorDefinition ad = context.getContext().fetchResource(ActorDefinition.class, anActor.getCanonical()); 446 boolean existingActor = ob.compare != null && ob.compare.actors.contains(anActor); 447 448 if (!firstActor) { 449 actorId.br(); 450 firstActor = false; 451 } 452 453 if (!existingActor) 454 actorId.style(STYLE_UNCHANGED); 455 if (ad == null) { 456 actorId.addText(anActor.getCanonical()); 457 } else { 458 actorId.ah(ad.getWebPath()).tx(ad.present()); 459 } 460 } 461 462 if (ob.compare != null) { 463 for (CanonicalType compActor : ob.compare.actors) { 464 if (!ob.actors.contains(compActor)) { 465 ActorDefinition compAd = context.getContext().fetchResource(ActorDefinition.class, compActor.toString()); 466 if (!firstActor) { 467 actorId.br(); 468 firstActor = true; 469 } 470 actorId = actorId.span(STYLE_REMOVED, null); 471 if (compAd == null) { 472 actorId.ah(context.prefixLocalHref(compActor.toString()), compActor.toString()).tx(compActor.toString()); 473 } else if (compAd.hasWebPath()) { 474 actorId.ah(context.prefixLocalHref(compAd.getWebPath()), compActor.toString()).tx(compAd.present()); 475 } else { 476 actorId.span(null, compActor.toString()).tx(compAd.present()); 477 } 478 } 479 } 480 } 481 } 482 483 484 if (hasElementId) { 485 XhtmlNode elementIds = tr.td().style("font-size: 11px"); 486 if (ob.compare!=null && ob.elementIds.equals(ob.compare.elementIds)) 487 elementIds.style(STYLE_UNCHANGED); 488 for (String eid : ob.elementIds) { 489 elementIds.sep(", "); 490 ElementDefinition ed = profile.getSnapshot().getElementById(eid); 491 if (ed != null) { 492 boolean inScope = inScopePaths.contains(ed.getPath()); 493 String name = eid.substring(eid.indexOf(".") + 1); 494 if (ed != null && inScope) { 495 String link = defPath + "#" + anchorPrefix + eid; 496 elementIds.ah(context.prefixLocalHref(link)).tx(name); 497 } else { 498 elementIds.code().tx(name); 499 } 500 } 501 } 502 503 if (ob.compare!=null && !ob.compare.elementIds.isEmpty()) { 504 for (String eid : ob.compare.elementIds) { 505 if (!ob.elementIds.contains(eid)) { 506 elementIds.sep(", "); 507 elementIds.span(STYLE_REMOVED, null).code().tx(eid); 508 } 509 } 510 } 511 } 512 if (hasUsage) { 513 if (ob.usage != null) { 514 boolean first = true; 515 XhtmlNode td = tr.td(); 516 for (UsageContext u : ob.usage) { 517 if (first) first = false; else td.tx(", "); 518 new DataRenderer(context).renderDataType(status, td, wrapWC(res, u)); 519 } 520 } else { 521 tr.td(); 522 } 523 } 524 if (hasDoco) { 525 if (ob.doco != null) { 526 String d = fullDoco ? md.processMarkdown("Obligation.documentation", ob.doco) : ob.docoShort; 527 String oldD = ob.compare==null ? null : fullDoco ? md.processMarkdown("Binding.description.compare", ob.compare.doco) : ob.compare.docoShort; 528 tr.td().style("font-size: 11px").innerHTML(compareHtml(d, oldD)); 529 } else { 530 tr.td().style("font-size: 11px"); 531 } 532 } 533 534 if (hasFilter) { 535 if (ob.filter != null) { 536 String d = "<code>"+ob.filter+"</code>" + (fullDoco ? md.processMarkdown("Binding.description", ob.filterDoco) : ""); 537 String oldD = ob.compare==null ? null : "<code>"+ob.compare.filter+"</code>" + (fullDoco ? md.processMarkdown("Binding.description", ob.compare.filterDoco) : ""); 538 tr.td().style("font-size: 11px").innerHTML(compareHtml(d, oldD)); 539 } else { 540 tr.td().style("font-size: 11px"); 541 } 542 } 543 if (hasSource) { 544 if (ob.source != null && !ob.source.equals(profile.getVersionedUrl())) { 545 StructureDefinition sd = context.getContext().fetchResource(StructureDefinition.class, ob.source); 546 var td = tr.td().style("font-size: 11px"); 547 td.tx("from "); 548 if (sd != null) { 549 td.ah(sd.getWebPath()).tx(sd.present()); 550 } else { 551 td.code().tx(ob.source); 552 } 553 } else { 554 tr.td().style("font-size: 11px"); 555 } 556 } 557 558 } 559 } 560 561 private XhtmlNode compareString(XhtmlNode node, String newS, String oldS) { 562 if (oldS==null) 563 return node.tx(newS); 564 if (newS.equals(oldS)) 565 return node.style(STYLE_UNCHANGED).tx(newS); 566 node.tx(newS); 567 node.br(); 568 return node.span(STYLE_REMOVED,null).tx(oldS); 569 } 570 571 private String compareHtml(String newS, String oldS) { 572 if (oldS==null) 573 return newS; 574 if (newS.equals(oldS)) 575 return "<span style=\"" + STYLE_UNCHANGED + "\">" + newS + "</span>"; 576 return newS + "<br/><span style=\"" + STYLE_REMOVED + "\">" + oldS + "</span>"; 577 } 578 579 private void renderCodes(XhtmlNodeList children, List<String> codes) { 580 581 if (!codes.isEmpty()) { 582 boolean first = true; 583 for (String code : codes) { 584 if (first) first = false; else children.tx(" & "); 585 int i = code.indexOf(":"); 586 if (i > -1) { 587 String c = code.substring(0, i); 588 code = code.substring(i+1); 589 children.b().tx(c.toUpperCase()); 590 children.tx(":"); 591 } 592 CodeResolution cr = this.cr.resolveCode("http://hl7.org/fhir/CodeSystem/obligation", code); 593 if (cr == null) { 594 cr = this.cr.resolveCode("http://hl7.org/fhir/tools/CodeSystem/obligation", code); 595 } 596 code = code.replace("will-", "").replace("can-", ""); 597 if (cr.getLink() != null) { 598 children.ah(context.prefixLocalHref(cr.getLink()), cr.getHint()).tx(code); 599 } else { 600 children.span(null, cr.getHint()).tx(code); 601 } 602 } 603 } else { 604 children.span(null, "No Obligation Code?").tx("??"); 605 } 606 } 607 608 public boolean hasObligations() { 609 return !obligations.isEmpty(); 610 } 611 612 private String displayForUsage(Coding c) { 613 if (c.hasDisplay()) { 614 return c.getDisplay(); 615 } 616 if ("http://terminology.hl7.org/CodeSystem/usage-context-type".equals(c.getSystem())) { 617 return c.getCode(); 618 } 619 return c.getCode(); 620 } 621 622}