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