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