001package org.hl7.fhir.r5.renderers; 002 003import java.io.IOException; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.List; 007import java.util.Map; 008 009import org.hl7.fhir.exceptions.DefinitionException; 010import org.hl7.fhir.exceptions.FHIRException; 011import org.hl7.fhir.exceptions.FHIRFormatError; 012import org.hl7.fhir.r5.comparison.VersionComparisonAnnotation; 013import org.hl7.fhir.r5.model.BooleanType; 014import org.hl7.fhir.r5.model.CanonicalResource; 015import org.hl7.fhir.r5.model.CodeSystem; 016import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode; 017import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent; 018import org.hl7.fhir.r5.model.CodeSystem.CodeSystemHierarchyMeaning; 019import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; 020import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent; 021import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent; 022import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent; 023import org.hl7.fhir.r5.model.Coding; 024import org.hl7.fhir.r5.model.Enumeration; 025import org.hl7.fhir.r5.model.Extension; 026import org.hl7.fhir.r5.model.PrimitiveType; 027import org.hl7.fhir.r5.model.Resource; 028import org.hl7.fhir.r5.model.StringType; 029import org.hl7.fhir.r5.renderers.CodeSystemRenderer.Translateable; 030import org.hl7.fhir.r5.renderers.utils.RenderingContext; 031import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType; 032import org.hl7.fhir.r5.renderers.utils.RenderingContext.MultiLanguagePolicy; 033import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; 034import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; 035import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.CodeSystemNavigator; 036import org.hl7.fhir.r5.utils.ToolingExtensions; 037import org.hl7.fhir.utilities.LoincLinker; 038import org.hl7.fhir.utilities.Utilities; 039import org.hl7.fhir.utilities.i18n.I18nConstants; 040import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 041import org.hl7.fhir.utilities.xhtml.XhtmlNode; 042 043public class CodeSystemRenderer extends TerminologyRenderer { 044 045 public class Translateable { 046 047 private String lang; 048 private StringType value; 049 050 public Translateable(String lang, StringType value) { 051 this.lang = lang; 052 this.value = value; 053 } 054 055 public String getLang() { 056 return lang; 057 } 058 059 public StringType getValue() { 060 return value; 061 } 062 063 } 064 065 066 private Boolean doMarkdown = null; 067 068 public CodeSystemRenderer(RenderingContext context) { 069 super(context); 070 } 071 072 public CodeSystemRenderer(RenderingContext context, ResourceContext rcontext) { 073 super(context, rcontext); 074 } 075 076 077 public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException { 078 return render(x, (CodeSystem) dr); 079 } 080 081 public boolean render(XhtmlNode x, CodeSystem cs) throws FHIRFormatError, DefinitionException, IOException { 082 boolean hasExtensions = false; 083 084 if (context.isHeader()) { 085 XhtmlNode h = x.h2(); 086 h.addText(cs.hasTitle() ? cs.getTitle() : cs.getName()); 087 addMarkdown(x, cs.getDescription()); 088 if (cs.hasCopyright()) 089 generateCopyright(x, cs ); 090 } 091 092 boolean props = generateProperties(x, cs); 093 generateFilters(x, cs); 094 List<UsedConceptMap> maps = new ArrayList<UsedConceptMap>(); 095 hasExtensions = generateCodeSystemContent(x, cs, hasExtensions, maps, props); 096 097 return hasExtensions; 098 } 099 100 public void describe(XhtmlNode x, CodeSystem cs) { 101 x.tx(display(cs)); 102 } 103 104 public String display(CodeSystem cs) { 105 return cs.present(); 106 } 107 108 private void generateFilters(XhtmlNode x, CodeSystem cs) { 109 if (cs.hasFilter()) { 110 x.para().b().tx(formatPhrase(RenderingContext.CODESYSTEM_FILTERS)); 111 XhtmlNode tbl = x.table("grid"); 112 XhtmlNode tr = tbl.tr(); 113 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_CODE)); 114 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_DESC)); 115 tr.td().b().tx(formatPhrase(RenderingContext.CODESYSTEM_FILTER_OP)); 116 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_VALUE)); 117 for (CodeSystemFilterComponent f : cs.getFilter()) { 118 tr = tbl.tr(); 119 renderStatus(f, tr.td()).tx(f.getCode()); 120 renderStatus(f.getDescriptionElement(), tr.td()).tx(f.getDescription()); 121 XhtmlNode td = tr.td(); 122 for (Enumeration<org.hl7.fhir.r5.model.Enumerations.FilterOperator> t : f.getOperator()) 123 renderStatus(t, td).tx(t.asStringValue()+" "); 124 renderStatus(f.getValueElement(), tr.td()).tx(f.getValue()); 125 } 126 } 127 } 128 129 private boolean generateProperties(XhtmlNode x, CodeSystem cs) { 130 if (cs.hasProperty()) { 131 boolean hasRendered = false; 132 boolean hasURI = false; 133 boolean hasDescription = false; 134 for (PropertyComponent p : cs.getProperty()) { 135 hasRendered = hasRendered || getDisplayForProperty(p) != null; 136 hasURI = hasURI || p.hasUri(); 137 hasDescription = hasDescription || p.hasDescription(); 138 } 139 140 x.para().b().tx(formatPhrase(RenderingContext.GENERAL_PROPS)); 141 x.para().b().tx(formatPhrase(RenderingContext.CODESYSTEM_PROPS_DESC)); 142 XhtmlNode tbl = x.table("grid"); 143 XhtmlNode tr = tbl.tr(); 144 if (hasRendered) { 145 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_NAME)); 146 } 147 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_CODE)); 148 if (hasURI) { 149 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_URI)); 150 } 151 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_TYPE)); 152 if (hasDescription) { 153 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_DESC)); 154 } 155 for (PropertyComponent p : cs.getProperty()) { 156 tr = tbl.tr(); 157 if (hasRendered) { 158 tr.td().tx(getDisplayForProperty(p)); 159 } 160 renderStatus(p, tr.td()).tx(p.getCode()); 161 if (hasURI) { 162 renderStatus(p.getUriElement(), tr.td()).tx(p.getUri()); 163 } 164 renderStatus(p.getTypeElement(), tr.td()).tx(p.hasType() ? p.getType().toCode() : ""); 165 if (hasDescription) { 166 renderStatus(p.getDescriptionElement(), tr.td()).tx(p.getDescription()); 167 } 168 } 169 return true; 170 } else { 171 return false; 172 } 173 } 174 175 private String sentenceForContent(CodeSystemContentMode mode, CodeSystem cs) { 176 if (mode == null) { 177 return formatPhrase(RenderingContext.CODESYSTEM_CONTENT_NOTPRESENT); 178 } 179 switch (mode) { 180 case COMPLETE: return formatPhrase(RenderingContext.CODESYSTEM_CONTENT_COMPLETE); 181 case EXAMPLE: return formatPhrase(RenderingContext.CODESYSTEM_CONTENT_EXAMPLE); 182 case FRAGMENT: return formatPhrase(RenderingContext.CODESYSTEM_CONTENT_FRAGMENT); 183 case NOTPRESENT: return formatPhrase(RenderingContext.CODESYSTEM_CONTENT_NOTPRESENT); 184 case SUPPLEMENT: 185 boolean properties = CodeSystemUtilities.hasProperties(cs); 186 boolean designations = CodeSystemUtilities.hasDesignations(cs); 187 String features; 188 if (properties && designations) { 189 features = (context.formatPhrase(RenderingContext.CODE_SYS_DISP_PROP)); 190 } else if (properties) { 191 features = (context.formatPhrase(RenderingContext.CODE_SYS_PROP)); 192 } else if (designations) { 193 features = (context.formatPhrase(RenderingContext.CODE_SYS_DISP)); 194 } else { 195 features = (context.formatPhrase(RenderingContext.CODE_SYS_FEAT)); // ? 196 } 197 return formatPhrase(RenderingContext.CODESYSTEM_CONTENT_SUPPLEMENT, features); 198 default: 199 throw new FHIRException(context.formatPhrase(RenderingContext.CODE_SYS_UNKN_MODE)); 200 } 201 } 202 203 private boolean generateCodeSystemContent(XhtmlNode x, CodeSystem cs, boolean hasExtensions, List<UsedConceptMap> maps, boolean props) throws FHIRFormatError, DefinitionException, IOException { 204 if (props) { 205 x.para().b().tx(formatPhrase(RenderingContext.CODESYSTEM_CONCEPTS)); 206 } 207 XhtmlNode p = x.para(); 208 renderStatus(cs.getUrlElement(), p.param("cs")).code().tx(cs.getUrl()); 209 makeCasedParam(p.param("cased"), cs, cs.getCaseSensitiveElement()); 210 makeHierarchyParam(p.param("h"), cs, cs.getHierarchyMeaningElement()); 211 212 p.paramValue("code-count", CodeSystemUtilities.countCodes(cs)); 213 p.sentenceForParams(sentenceForContent(cs.getContent(), cs)); 214 if (cs.getContent() == CodeSystemContentMode.NOTPRESENT) { 215 return false; 216 } 217 218 XhtmlNode t = x.table( "codes"); 219 boolean definitions = false; 220 boolean commentS = false; 221 boolean deprecated = false; 222 boolean display = false; 223 boolean hierarchy = false; 224 boolean version = false; 225 boolean ignoreStatus = false; 226 boolean isSupplement = cs.getContent() == CodeSystemContentMode.SUPPLEMENT; 227 List<PropertyComponent> properties = new ArrayList<>(); 228 for (PropertyComponent cp : cs.getProperty()) { 229 if (showPropertyInTable(cp)) { 230 boolean exists = false; 231 for (ConceptDefinitionComponent c : cs.getConcept()) { 232 exists = exists || conceptsHaveProperty(c, cp); 233 } 234 if (exists) { 235 properties.add(cp); 236 if ("status".equals(cp.getCode())) { 237 ignoreStatus = true; 238 } 239 } 240 } 241 } 242 List<String> langs = new ArrayList<>(); 243 for (ConceptDefinitionComponent c : cs.getConcept()) { 244 commentS = commentS || conceptsHaveComments(c); 245 deprecated = deprecated || conceptsHaveDeprecated(cs, c, ignoreStatus); 246 display = display || conceptsHaveDisplay(c); 247 version = version || conceptsHaveVersion(c); 248 hierarchy = hierarchy || c.hasConcept(); 249 definitions = definitions || conceptsHaveDefinition(c); 250 listConceptLanguages(cs, c, langs); 251 } 252 CodeSystemNavigator csNav = new CodeSystemNavigator(cs); 253 hierarchy = hierarchy || csNav.isRestructure(); 254 255 if (langs.size() < 2) { 256 addCopyColumn(addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, definitions, commentS, version, deprecated, properties, langs, null, true), maps)); 257 } else { 258 addCopyColumn(addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, definitions, commentS, version, deprecated, properties, null, null, false), maps)); 259 } 260 for (ConceptDefinitionComponent c : csNav.getConcepts(null)) { 261 hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, definitions, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs.size() < 2 ? langs : null, isSupplement) || hasExtensions; 262 } 263 if (langs.size() >= 2) { 264 Collections.sort(langs); 265 x.para().b().tx(context.formatPhrase(RenderingContext.GENERAL_ADD_LANG)); 266 t = x.table("codes"); 267 XhtmlNode tr = t.tr(); 268 tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); 269 for (String lang : langs) 270 tr.td().b().addText(describeLang(lang)); 271 for (ConceptDefinitionComponent c : cs.getConcept()) { 272 addLanguageRow(c, t, langs); 273 } 274 } 275 return hasExtensions; 276 } 277 278 private void makeHierarchyParam(XhtmlNode x, CodeSystem cs, Enumeration<CodeSystemHierarchyMeaning> hm) { 279 if (hm.hasValue()) { 280 String s = hm.getValue().getDisplay(); 281 renderStatus(hm, x).tx(" "+context.formatPhrase(RenderingContext.CODE_SYS_IN_A_HIERARCHY, s)); 282 } else if (VersionComparisonAnnotation.hasDeleted(cs, "hierarchyMeaning")) { 283 makeHierarchyParam(x, null, (Enumeration<CodeSystemHierarchyMeaning>) VersionComparisonAnnotation.getDeleted(cs, "hierarchyMeaning").get(0)); 284 } else if (CodeSystemUtilities.hasHierarchy(cs)) { 285 x.tx(" "+ (context.formatPhrase(RenderingContext.CODE_SYS_UNDEF_HIER))); 286 } else { 287 x.tx(""); 288 } 289 } 290 291 private void makeCasedParam(XhtmlNode x, CodeSystem cs, BooleanType caseSensitiveElement) { 292 if (caseSensitiveElement.hasValue()) { 293 String s = caseSensitiveElement.getValue() == true? "case-sensitive" : "case-insensitive"; 294 renderStatus(caseSensitiveElement, x).tx(s); 295 } else if (VersionComparisonAnnotation.hasDeleted(cs, "caseSensitive")) { 296 makeCasedParam(x, null, (BooleanType) VersionComparisonAnnotation.getDeleted(cs, "caseSensitive").get(0)); 297 } else { 298 x.tx(""); 299 } 300 } 301 302 private void listConceptLanguages(CodeSystem cs, ConceptDefinitionComponent c, List<String> langs) { 303 for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { 304 if (cd.hasLanguage() && !langs.contains(cd.getLanguage()) && (!cs.hasLanguage() || !cs.getLanguage().equals(cd.getLanguage()))) { 305 langs.add(cd.getLanguage()); 306 } 307 } 308 309 for (ConceptDefinitionComponent g : c.getConcept()) { 310 listConceptLanguages(cs, g, langs); 311 } 312 } 313 314 private void addCopyColumn(XhtmlNode tr) { 315 if (context.isCopyButton()) { 316 tr.td().b().tx(context.formatPhrase(RenderingContext.CODE_SYS_COPY)); 317 } 318 319 } 320 321 private boolean conceptsHaveDefinition(ConceptDefinitionComponent c) { 322 if (c.hasDefinition()) { 323 return true; 324 } 325 for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { 326 if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { 327 return true; 328 } 329 } 330 for (ConceptDefinitionComponent g : c.getConcept()) { 331 if (conceptsHaveDefinition(g)) { 332 return true; 333 } 334 } 335 return false; 336 } 337 338 private boolean conceptsHaveProperty(ConceptDefinitionComponent c, PropertyComponent cp) { 339 if (CodeSystemUtilities.hasProperty(c, cp.getCode())) 340 return true; 341 for (ConceptDefinitionComponent g : c.getConcept()) 342 if (conceptsHaveProperty(g, cp)) 343 return true; 344 return false; 345 346 } 347 348 private boolean showPropertyInTable(PropertyComponent cp) { 349 if (cp.hasCode()) { 350 if (cp.hasExtension(ToolingExtensions.EXT_RENDERED_VALUE)) { 351 return true; 352 } 353 if (cp.getCodeElement().hasExtension(ToolingExtensions.EXT_RENDERED_VALUE)) { 354 return true; 355 } 356 String uri = cp.getUri(); 357 if (Utilities.noString(uri)){ 358 return true; // do we always want to render properties in this case? Not sure... 359 } 360 String code = null; 361 if (uri.contains("#")) { 362 code = uri.substring(uri.indexOf("#")+1); 363 uri = uri.substring(0, uri.indexOf("#")); 364 } 365 if (Utilities.existsInList(uri, "http://hl7.org/fhir/concept-properties") || context.getCodeSystemPropList().contains(uri)) { 366 return true; 367 }; 368 CodeSystem cs = getContext().getWorker().fetchCodeSystem(uri); 369 if (cs == null) { 370 return false; 371 } 372 return code == null ? false : CodeSystemUtilities.hasCode(cs, code); 373 } 374 return false; 375 } 376 377 378 private int countConcepts(List<ConceptDefinitionComponent> list) { 379 int count = list.size(); 380 for (ConceptDefinitionComponent c : list) 381 if (c.hasConcept()) 382 count = count + countConcepts(c.getConcept()); 383 return count; 384 } 385 386 private boolean conceptsHaveComments(ConceptDefinitionComponent c) { 387 if (ToolingExtensions.hasCSComment(c)) 388 return true; 389 for (ConceptDefinitionComponent g : c.getConcept()) 390 if (conceptsHaveComments(g)) 391 return true; 392 return false; 393 } 394 395 private boolean conceptsHaveDisplay(ConceptDefinitionComponent c) { 396 if (c.hasDisplay() && !c.getDisplay().equals(c.getCode())) 397 return true; 398 for (ConceptDefinitionComponent g : c.getConcept()) 399 if (conceptsHaveDisplay(g)) 400 return true; 401 return false; 402 } 403 404 private boolean conceptsHaveVersion(ConceptDefinitionComponent c) { 405 if (c.hasUserData("cs.version.notes")) 406 return true; 407 for (ConceptDefinitionComponent g : c.getConcept()) 408 if (conceptsHaveVersion(g)) 409 return true; 410 return false; 411 } 412 413 private boolean conceptsHaveDeprecated(CodeSystem cs, ConceptDefinitionComponent c, boolean ignoreStatus) { 414 if (CodeSystemUtilities.isDeprecated(cs, c, ignoreStatus)) 415 return true; 416 for (ConceptDefinitionComponent g : c.getConcept()) 417 if (conceptsHaveDeprecated(cs, g, ignoreStatus)) 418 return true; 419 return false; 420 } 421 422 423 424 private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, boolean hasDefinitions, boolean comment, boolean version, boolean deprecated, List<UsedConceptMap> maps, String system, CodeSystem cs, List<PropertyComponent> properties, CodeSystemNavigator csNav, List<String> langs, boolean isSupplement) throws FHIRFormatError, DefinitionException, IOException { 425 boolean hasExtensions = false; 426 XhtmlNode tr = t.tr(); 427 boolean notCurrent = CodeSystemUtilities.isNotCurrent(cs, c); 428 if (notCurrent) { 429 tr.setAttribute("style", "background-color: #ffeeee"); 430 } 431 432 XhtmlNode td = renderStatusRow(c, t, tr); 433 if (hasHierarchy) { 434 td.addText(Integer.toString(level+1)); 435 td = tr.td(); 436 String s = Utilities.padLeft("", '\u00A0', level*2); 437 td.addText(s); 438 } 439 String link = isSupplement ? getLinkForCode(cs.getSupplements(), null, c.getCode()) : null; 440 if (link != null) { 441 td.ah(link).style( "white-space:nowrap").addText(c.getCode()); 442 } else { 443 td.style("white-space:nowrap").addText(c.getCode()); 444 } 445 XhtmlNode a; 446 if (c.hasCodeElement()) { 447 td.an(cs.getId()+"-" + Utilities.nmtokenize(c.getCode())); 448 } 449 450 if (hasDisplay) { 451 td = tr.td(); 452 hasExtensions = renderDisplayName(c, cs, td, langs) || hasExtensions; 453 } 454 if (hasDefinitions) { 455 td = tr.td(); 456 if (c != null &&c.hasDefinitionElement()) { 457 // translations of the definition might come from either the translation extension, or from the designations 458 StringType defn = context.getTranslatedElement(c.getDefinitionElement()); 459 boolean sl = false; 460 for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { 461 if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { 462 sl = true; 463 } 464 } 465 466 if (getContext().getMultiLanguagePolicy() == MultiLanguagePolicy.NONE || !(sl || ToolingExtensions.hasLanguageTranslations(defn))) { 467 if (hasMarkdownInDefinitions(cs)) { 468 addMarkdown(renderStatusDiv(defn, td), defn.asStringValue()); 469 } else { 470 renderStatus(defn, td).addText(defn.asStringValue()); 471 } 472 } else { 473 List<Translateable> list = new ArrayList<>(); 474 list.add(new Translateable(cs.getLanguage(), defn)); 475 for (Extension ext : defn.getExtensionsByUrl(ToolingExtensions.EXT_TRANSLATION)) { 476 hasExtensions = true; 477 list.add(new Translateable(ext.getExtensionString("lang"), ext.getExtensionByUrl("content").getValueStringType())); 478 } 479 for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { 480 if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { 481 list.add(new Translateable(cd.getLanguage(), cd.getValueElement())); 482 } 483 } 484 boolean first = true; 485 for (Translateable ti : list) { 486 if (first) { 487 first = false; 488 } else { 489 td.br(); 490 } 491 492 if (ti.lang != null) { 493 td.addText(ti.lang + ": "); 494 } 495 if (hasMarkdownInDefinitions(cs)) { 496 addMarkdown(renderStatusDiv(ti.getValue(), td), ti.getValue().asStringValue()); 497 } else { 498 renderStatus(ti.getValue(), td).addText(ti.getValue().asStringValue()); 499 } 500 } 501 } 502 } 503 } 504 if (deprecated) { 505 td = tr.td(); 506 Boolean b = CodeSystemUtilities.isDeprecated(cs, c, false); 507 if (b != null && b) { 508 smartAddText(td, formatPhrase(RenderingContext.CODESYSTEM_DEPRECATED)); 509 hasExtensions = true; 510 if (ToolingExtensions.hasExtension(c, ToolingExtensions.EXT_REPLACED_BY)) { 511 Coding cc = (Coding) ToolingExtensions.getExtension(c, ToolingExtensions.EXT_REPLACED_BY).getValue(); 512 td.tx(" "+ context.formatPhrase(RenderingContext.CODE_SYS_REPLACED_BY) + " "); 513 String url = getCodingReference(cc, system); 514 if (url != null) { 515 td.ah(url).addText(cc.getCode()); 516 td.tx(": "+cc.getDisplay()+")"); 517 } else 518 td.addText(cc.getCode()+" '"+cc.getDisplay()+"' in "+cc.getSystem()+")"); 519 } else { 520 Extension ext = c.getExtensionByUrl(ToolingExtensions.EXT_STANDARDS_STATUS); 521 if (ext != null) { 522 ext = ext.getValue().getExtensionByUrl(ToolingExtensions.EXT_STANDARDS_STATUS_REASON); 523 if (ext != null) { 524 addMarkdown(td, ext.getValue().primitiveValue()); 525 } 526 } 527 } 528 } 529 } 530 if (comment) { 531 td = tr.td(); 532 Extension ext = c.getExtensionByUrl(ToolingExtensions.EXT_CS_COMMENT); 533 if (ext != null && ext.hasValue() && ext.getValue().primitiveValue() != null) { 534 hasExtensions = true; 535 StringType defn = context.getTranslatedElement((PrimitiveType<?>) ext.getValue()); 536 if (getContext().getMultiLanguagePolicy() == MultiLanguagePolicy.NONE ||!(ToolingExtensions.hasLanguageTranslations(ext.getValue()))) { 537 td.addText(defn.asStringValue()); 538 } else { 539 List<Translateable> list = new ArrayList<>(); 540 list.add(new Translateable(cs.getLanguage(), defn)); 541 for (Extension ex : defn.getExtensionsByUrl(ToolingExtensions.EXT_TRANSLATION)) { 542 hasExtensions = true; 543 list.add(new Translateable(ex.getExtensionString("lang"), ex.getExtensionByUrl("content").getValueStringType())); 544 } 545 boolean first = true; 546 for (Translateable ti : list) { 547 if (first) { 548 first = false; 549 } else { 550 td.br(); 551 } 552 553 if (ti.lang != null) { 554 td.addText(ti.lang + ": "); 555 } 556 renderStatus(ti.getValue(), td).addText(ti.getValue().asStringValue()); 557 } 558 } 559 } 560 } 561 if (version) { 562 td = tr.td(); 563 if (c.hasUserData("cs.version.notes")) 564 td.addText(c.getUserString("cs.version.notes")); 565 } 566 if (properties != null) { 567 for (PropertyComponent pc : properties) { 568 td = tr.td(); 569 boolean first = true; 570 List<ConceptPropertyComponent> pcvl = CodeSystemUtilities.getPropertyValues(c, pc.getCode()); 571 for (ConceptPropertyComponent pcv : pcvl) { 572 if (pcv.hasValue()) { 573 if (first) first = false; else td.addText(", "); 574 if (pcv.hasValueCoding()) { 575 td.addText(pcv.getValueCoding().getCode()); 576 } else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrl(pcv.getValue().primitiveValue())) { 577 CanonicalResource cr = (CanonicalResource) context.getContext().fetchResource(Resource.class, pcv.getValue().primitiveValue()); 578 if (cr != null) { 579 td.ah(cr.getWebPath(), cr.getVersionedUrl()).tx(cr.present()); 580 } else if (Utilities.isAbsoluteUrlLinkable(pcv.getValue().primitiveValue())) { 581 td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue()); 582 } else { 583 td.code(pcv.getValue().primitiveValue()); 584 } 585 } else if ("parent".equals(pcv.getCode())) { 586 td.ah("#"+cs.getId()+"-"+Utilities.nmtokenize(pcv.getValue().primitiveValue())).addText(pcv.getValue().primitiveValue()); 587 } else { 588 td.addText(pcv.getValue().primitiveValue()); 589 } 590 } 591 } 592 } 593 } 594 595 if (langs != null) { 596 for (String lang : langs) { 597 td = tr.td().tx(getDisplay(lang, c)); 598 } 599 } 600 for (UsedConceptMap m : maps) { 601 td = tr.td(); 602 List<TargetElementComponentWrapper> mappings = findMappingsForCode(c.getCode(), m.getMap()); 603 boolean first = true; 604 for (TargetElementComponentWrapper mapping : mappings) { 605 if (!first) 606 td.br(); 607 first = false; 608 XhtmlNode span = td.span(null, mapping.comp.hasRelationship() ? mapping.comp.getRelationship().toCode() : ""); 609 span.addText(getCharForRelationship(mapping.comp)); 610 a = td.ah(getContext().getLink(KnownLinkType.SPEC)+m.getLink()+"#"+makeAnchor(mapping.group.getTarget(), mapping.comp.getCode())); 611 a.addText(mapping.comp.getCode()); 612 if (!Utilities.noString(mapping.comp.getComment())) 613 td.i().tx("("+mapping.comp.getComment()+")"); 614 } 615 } 616 List<ConceptDefinitionComponent> ocl = csNav.getOtherChildren(c); 617 for (ConceptDefinitionComponent cc : csNav.getConcepts(c)) { 618 hasExtensions = addDefineRowToTable(t, cc, level+1, hasHierarchy, hasDisplay, hasDefinitions, comment, version, deprecated, maps, system, cs, properties, csNav, langs, isSupplement) || hasExtensions; 619 } 620 for (ConceptDefinitionComponent cc : ocl) { 621 tr = t.tr(); 622 td = tr.td(); 623 td.addText(Integer.toString(level+2)); 624 td = tr.td(); 625 String s = Utilities.padLeft("", '\u00A0', (level+1)*2); 626 td.addText(s); 627 td.style("white-space:nowrap"); 628 a = td.ah("#"+cs.getId()+"-" + Utilities.nmtokenize(cc.getCode())); 629 a.addText(cc.getCode()); 630 if (hasDisplay) { 631 td = tr.td(); 632 hasExtensions = renderDisplayName(cc, cs, td, langs) || hasExtensions; 633 } 634 int w = 1 + (deprecated ? 1 : 0) + (comment ? 1 : 0) + (version ? 1 : 0) + maps.size(); 635 if (properties != null) { 636 w = w + properties.size(); 637 } 638 td = tr.td().colspan(Integer.toString(w)); 639 } 640 if (context.isCopyButton()) { 641 td = tr.td(); 642 clipboard(td, "icon_clipboard_x.png", "XML", "<system value=\""+Utilities.escapeXml(cs.getUrl())+"\">\n"+(cs.getVersionNeeded() ? "<version value=\""+Utilities.escapeXml(cs.getVersion())+"\">\n" : "")+"<code value=\""+Utilities.escapeXml(c.getCode())+"\">\n<display value=\""+Utilities.escapeXml(c.getDisplay())+"\">\n"); 643 td.nbsp(); 644 clipboard(td, "icon_clipboard_j.png", "JSON", "\"system\" : \""+Utilities.escapeXml(cs.getUrl())+"\",\n"+(cs.getVersionNeeded() ? "\"version\" : \""+Utilities.escapeXml(cs.getVersion())+"\",\n" : "")+"\"code\" : \""+Utilities.escapeXml(c.getCode())+"\",\n\"display\" : \""+Utilities.escapeXml(c.getDisplay())+"\"\n"); 645 } 646 return hasExtensions; 647 } 648 649 private String getDisplay(String lang, ConceptDefinitionComponent c) { 650 for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { 651 if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "display") && cd.hasLanguage() && cd.getLanguage().equals(lang)) { 652 return cd.getValue(); 653 } 654 } 655 for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { 656 if (cd.hasLanguage() && cd.getLanguage().equals(lang)) { 657 return cd.getValue(); 658 } 659 } 660 return null; 661 } 662 663 private boolean hasMarkdownInDefinitions(CodeSystem cs) { 664 if (doMarkdown == null) { 665 if (cs.hasExtension("http://hl7.org/fhir/StructureDefinition/codesystem-use-markdown")) { 666 doMarkdown = ToolingExtensions.readBoolExtension(cs, "http://hl7.org/fhir/StructureDefinition/codesystem-use-markdown"); 667 } else { 668 doMarkdown = CodeSystemUtilities.hasMarkdownInDefinitions(cs, context.getMarkdown()); 669 } 670 } 671 return doMarkdown; 672 } 673 674 675 public boolean renderDisplayName(ConceptDefinitionComponent c, CodeSystem cs, XhtmlNode td, List<String> langs) { 676 boolean hasExtensions = false; 677 if (c.hasDisplayElement()) { 678 StringType disp = c.getDisplayElement(); 679 List<Translateable> list = new ArrayList<>(); 680 list.add(new Translateable(cs.getLanguage(), disp)); 681 for (Extension ext : disp.getExtensionsByUrl(ToolingExtensions.EXT_TRANSLATION)) { 682 if (!langs.contains(ext.getExtensionString("lang"))) { 683 hasExtensions = true; 684 list.add(new Translateable(ext.getExtensionString("lang"), ext.getExtensionByUrl("content").getValueStringType())); 685 } 686 } 687 for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { 688 if (cd.hasLanguage() && (langs == null || !langs.contains(cd.getLanguage())) && (c.getDefinition() == null || !c.getDefinition().equalsIgnoreCase(cd.getValue()))) { 689 list.add(new Translateable(cd.getLanguage(), cd.getValueElement())); 690 } 691 } 692 693 if (getContext().getMultiLanguagePolicy() == MultiLanguagePolicy.NONE || list.size() <= 1) { 694 renderStatus(disp, td).addText(disp.asStringValue()); 695 } else { 696 boolean first = true; 697 for (Translateable ti : list) { 698 if (first) { 699 first = false; 700 } else { 701 td.br(); 702 } 703 704 if (ti.lang != null) { 705 td.addText(ti.lang + ": "); 706 } 707 renderStatus(ti.getValue(), td).addText(ti.getValue().asStringValue()); 708 } 709 710 } 711 } 712 return hasExtensions; 713 } 714 715 private String getCodingReference(Coding cc, String system) { 716 if (cc.getSystem().equals(system)) 717 return "#"+cc.getCode(); 718 if (cc.getSystem().equals("http://snomed.info/sct")) 719 return "http://snomed.info/sct/"+cc.getCode(); 720 if (cc.getSystem().equals("http://loinc.org")) 721 return LoincLinker.getLinkForCode(cc.getCode()); 722 return null; 723 } 724 725 726 private void addLanguageRow(ConceptDefinitionComponent c, XhtmlNode t, List<String> langs) { 727 XhtmlNode tr = t.tr(); 728 tr.td().addText(c.getCode()); 729 for (String lang : langs) { 730 ConceptDefinitionDesignationComponent d = null; 731 for (ConceptDefinitionDesignationComponent designation : c.getDesignation()) { 732 if (designation.hasLanguage()) { 733 if (lang.equals(designation.getLanguage())) 734 d = designation; 735 } 736 } 737 tr.td().addText(d == null ? "" : d.getValue()); 738 } 739 } 740 741}