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