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