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