
001package org.hl7.fhir.r5.renderers; 002 003import java.io.IOException; 004import java.io.UnsupportedEncodingException; 005import java.text.ParseException; 006import java.text.SimpleDateFormat; 007import java.util.ArrayList; 008import java.util.Collections; 009import java.util.Date; 010import java.util.HashMap; 011import java.util.HashSet; 012import java.util.List; 013import java.util.Map; 014import java.util.Set; 015 016import org.hl7.fhir.exceptions.DefinitionException; 017import org.hl7.fhir.exceptions.FHIRException; 018import org.hl7.fhir.exceptions.FHIRFormatError; 019import org.hl7.fhir.exceptions.TerminologyServiceException; 020import org.hl7.fhir.r5.comparison.VersionComparisonAnnotation; 021import org.hl7.fhir.r5.model.Base; 022import org.hl7.fhir.r5.model.CanonicalResource; 023import org.hl7.fhir.r5.model.CodeSystem; 024import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; 025import org.hl7.fhir.r5.model.Coding; 026import org.hl7.fhir.r5.model.ConceptMap; 027import org.hl7.fhir.r5.model.DataType; 028import org.hl7.fhir.r5.model.Enumerations.FilterOperator; 029import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; 030import org.hl7.fhir.r5.model.Expression; 031import org.hl7.fhir.r5.model.Extension; 032import org.hl7.fhir.r5.model.ExtensionHelper; 033import org.hl7.fhir.r5.model.PrimitiveType; 034import org.hl7.fhir.r5.model.Resource; 035import org.hl7.fhir.r5.model.StringType; 036import org.hl7.fhir.r5.model.UriType; 037import org.hl7.fhir.r5.model.ValueSet; 038import org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent; 039import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; 040import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent; 041import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; 042import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent; 043import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent; 044import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; 045import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; 046import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent; 047import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent; 048import org.hl7.fhir.r5.renderers.utils.RenderingContext; 049import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 050import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; 051import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; 052import org.hl7.fhir.r5.terminologies.ValueSetUtilities; 053import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; 054import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest; 055import org.hl7.fhir.r5.terminologies.utilities.SnomedUtilities; 056import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; 057import org.hl7.fhir.r5.utils.EOperationOutcome; 058import org.hl7.fhir.r5.utils.ToolingExtensions; 059import org.hl7.fhir.r5.utils.UserDataNames; 060import org.hl7.fhir.utilities.LoincLinker; 061import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 062import org.hl7.fhir.utilities.Utilities; 063import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; 064import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; 065import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; 066import org.hl7.fhir.utilities.xhtml.XhtmlNode; 067 068import com.google.common.collect.HashMultimap; 069import com.google.common.collect.Multimap; 070 071@MarkedToMoveToAdjunctPackage 072public class ValueSetRenderer extends TerminologyRenderer { 073 074 public ValueSetRenderer(RenderingContext context) { 075 super(context); 076 } 077 078 @Override 079 public void buildNarrative(RenderingStatus status, XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException, FHIRException, EOperationOutcome { 080 if (!r.isDirect()) { 081 // the intention is to change this in the future 082 x.para().tx("ValueSetRenderer only renders native resources directly"); 083 } else { 084 renderResourceTechDetails(r, x); 085 ValueSet vs = (ValueSet) r.getBase(); 086 genSummaryTable(status, x, vs); 087 List<UsedConceptMap> maps = findReleventMaps(vs); 088 089 if (context.isShowSummaryTable()) { 090 XhtmlNode h = x.h2(); 091 h.addText(vs.hasTitle() ? vs.getTitle() : vs.getName()); 092 addMarkdown(x, vs.getDescription()); 093 if (vs.hasCopyright()) 094 generateCopyright(x, r); 095 } 096 if (vs.hasExtension(ToolingExtensions.EXT_VS_CS_SUPPL_NEEDED)) { 097 var p = x.para(); 098 p.tx("This ValueSet requires the Code system Supplement "); 099 String u = ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_VS_CS_SUPPL_NEEDED); 100 CodeSystem cs = context.getContext().fetchResource(CodeSystem.class, u); 101 if (cs == null) { 102 p.code().tx(u); 103 } else if (!cs.hasWebPath()) { 104 p.ah(u).tx(cs.present()); 105 } else { 106 p.ah(cs.getWebPath()).tx(cs.present()); 107 } 108 p.tx("."); 109 } 110 if (vs.hasExtension(ToolingExtensions.EXT_VALUESET_PARAMETER)) { 111 x.para().b().tx("This ValueSet has parameters"); 112 XhtmlNode tbl = x.table("grid"); 113 XhtmlNode tr = tbl.tr(); 114 tr.th().tx("Name"); 115 tr.th().tx("Documentation"); 116 for (Extension ext : vs.getExtensionsByUrl(ToolingExtensions.EXT_VALUESET_PARAMETER)) { 117 tr = tbl.tr(); 118 tr.td().tx(ext.getExtensionString("name")); 119 tr.td().markdown(ext.getExtensionString("documentation"), "parameter.documentation"); 120 } 121 } 122 if (vs.hasExpansion()) { 123 // for now, we just accept an expansion if there is one 124 generateExpansion(status, r, x, vs, false, maps); 125 } else { 126 generateComposition(status, r, x, vs, false, maps); 127 } 128 } 129 } 130 131 132 @Override 133 public String buildSummary(ResourceWrapper r) throws UnsupportedEncodingException, IOException { 134 return canonicalTitle(r); 135 } 136 137 private static final int MAX_DESIGNATIONS_IN_LINE = 5; 138 139 private static final int MAX_BATCH_VALIDATION_SIZE = 1000; 140 141 private List<ConceptMapRenderInstructions> renderingMaps = new ArrayList<ConceptMapRenderInstructions>(); 142 143 144 public void render(RenderingStatus status, XhtmlNode x, ValueSet vs, boolean header) throws FHIRFormatError, DefinitionException, IOException { 145 146 } 147 148 public void describe(XhtmlNode x, ValueSet vs) { 149 x.tx(display(vs)); 150 } 151 152 public String display(ValueSet vs) { 153 return vs.present(); 154 } 155 156 157 private List<UsedConceptMap> findReleventMaps(ValueSet vs) throws FHIRException { 158 List<UsedConceptMap> res = new ArrayList<UsedConceptMap>(); 159 for (ConceptMap cm : getContext().getWorker().fetchResourcesByType(ConceptMap.class)) { 160 if (isSource(vs, cm.getSourceScope())) { 161 ConceptMapRenderInstructions re = findByTarget(cm.getTargetScope()); 162 if (re == null) { 163 re = new ConceptMapRenderInstructions(cm.present(), cm.getUrl(), false); 164 } 165 if (re != null) { 166 ValueSet vst = cm.hasTargetScope() ? getContext().getWorker().findTxResource(ValueSet.class, cm.hasTargetScopeCanonicalType() ? cm.getTargetScopeCanonicalType().getValue() : cm.getTargetScopeUriType().asStringValue(), cm) : null; 167 res.add(new UsedConceptMap(re, vst == null ? cm.getWebPath() : vst.getWebPath(), cm)); 168 } 169 } 170 } 171 return res; 172 173// @Override 174// public List<ConceptMap> findMapsForSource(String url) throws FHIRException { 175// synchronized (lock) { 176// List<ConceptMap> res = new ArrayList<ConceptMap>(); 177// for (ConceptMap map : maps.getList()) { 178// if (((Reference) map.getSourceScope()).getReference().equals(url)) { 179// res.add(map); 180// } 181// } 182// return res; 183// } 184// } 185 186// Map<ConceptMap, String> mymaps = new HashMap<ConceptMap, String>(); 187// for (ConceptMap a : context.getWorker().findMapsForSource(vs.getUrl())) { 188// String url = ""; 189// ValueSet vsr = context.getWorker().findTxResource(ValueSet.class, ((Reference) a.getTarget()).getReference()); 190// if (vsr != null) 191// url = (String) vsr.getUserData(UserDataNames.filename); 192// mymaps.put(a, url); 193// } 194// Map<ConceptMap, String> mymaps = new HashMap<ConceptMap, String>(); 195// for (ConceptMap a : context.getWorker().findMapsForSource(cs.getValueSet())) { 196// String url = ""; 197// ValueSet vsr = context.getWorker().fetchResource(ValueSet.class, ((Reference) a.getTarget()).getReference()); 198// if (vsr != null) 199// url = (String) vsr.getUserData(UserDataNames.filename); 200// mymaps.put(a, url); 201// } 202 // also, look in the contained resources for a concept map 203// for (Resource r : cs.getContained()) { 204// if (r instanceof ConceptMap) { 205// ConceptMap cm = (ConceptMap) r; 206// if (((Reference) cm.getSource()).getReference().equals(cs.getValueSet())) { 207// String url = ""; 208// ValueSet vsr = context.getWorker().findTxResource(ValueSet.class, ((Reference) cm.getTarget()).getReference()); 209// if (vsr != null) 210// url = (String) vsr.getUserData(UserDataNames.filename); 211// mymaps.put(cm, url); 212// } 213// } 214// } 215 } 216 217 private boolean isSource(ValueSet vs, DataType source) { 218 return vs.hasUrl() && source != null && vs.getUrl().equals(source.primitiveValue()); 219 } 220 221 private void generateExpansion(RenderingStatus status, ResourceWrapper res, XhtmlNode x, ValueSet vs, boolean header, List<UsedConceptMap> maps) throws FHIRFormatError, DefinitionException, IOException { 222 List<String> langs = new ArrayList<String>(); 223 Map<String, String> designations = new HashMap<>(); // map of url = description, where url is the designation code. Designations that are for languages won't make it into this list 224 Map<String, String> properties = new HashMap<>(); // map of url = description, where url is the designation code. Designations that are for languages won't make it into this list 225 226 if (header) { 227 XhtmlNode h = x.addTag(getHeader()); 228 h.tx(context.formatPhrase(RenderingContext.VALUE_SET_CONT)); 229 if (IsNotFixedExpansion(vs)) 230 addMarkdown(x, vs.getDescription()); 231 if (vs.hasCopyright()) 232 generateCopyright(x, res); 233 } 234 boolean hasFragment = generateContentModeNotices(x, vs.getExpansion(), vs); 235 generateVersionNotice(x, vs.getExpansion(), vs); 236 237 if (ToolingExtensions.hasExtension(vs.getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY)) { 238 String msg = null; 239 if (vs.getExpansion().getContains().isEmpty()) { 240 msg = context.formatPhrase(RenderingContext.VALUE_SET_TOO_COSTLY); 241 } else { 242 msg = context.formatPhrase(RenderingContext.VALUE_SET_CODE_SELEC, countMembership(vs)); 243 } 244 x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(msg); 245 } else { 246 int count = ValueSetUtilities.countExpansion(vs); 247 if (vs.getExpansion().hasTotal()) { 248 if (count != vs.getExpansion().getTotal()) { 249 x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px") 250 .addText(context.formatPhrase(hasFragment ? RenderingContext.VALUE_SET_HAS_AT_LEAST : RenderingContext.VALUE_SET_HAS, vs.getExpansion().getTotal(), count)); 251 } else { 252 x.para().tx(context.formatPhrase(hasFragment ? RenderingContext.VALUE_SET_CONTAINS_AT_LEAST : RenderingContext.VALUE_SET_CONTAINS, vs.getExpansion().getTotal())); 253 } 254 } else if (count == 1000) { 255 // it's possible that there's exactly 1000 codes, in which case wht we're about to do is wrong 256 // work in progress to tighten up the terminology system to always return a total... 257 String msg = context.formatPhrase(RenderingContext.VALUE_SET_SEL); 258 x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(msg); 259 } else { 260 x.para().tx(context.formatPhrase(RenderingContext.VALUE_SET_NUMBER_CONCEPTS, count)); 261 } 262 } 263 264 265 boolean doLevel = false; 266 for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) { 267 if (cc.hasContains()) { 268 doLevel = true; 269 break; 270 } 271 } 272 boolean doInactive = checkDoInactive(vs.getExpansion().getContains()); 273 boolean doDefinition = checkDoDefinition(vs.getExpansion().getContains()); 274 275 XhtmlNode t = x.table("codes", false); 276 XhtmlNode tr = t.tr(); 277 if (doLevel) 278 tr.td().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_LEVEL)); 279 tr.td().attribute("style", "white-space:nowrap").b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); 280 tr.td().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_SYSTEM)); 281 XhtmlNode tdDisp = tr.td(); 282 String displang = vs.getLanguage(); 283 if (displang == null) { 284 displang = findParamValue(vs.getExpansion().getParameter(), "displayLanguage"); 285 } 286 if (displang == null) { 287 tdDisp.b().tx(context.formatPhrase(RenderingContext.TX_DISPLAY)); 288 } else { 289 tdDisp.b().tx(context.formatPhrase(RenderingContext.TX_DISPLAY_LANG, displang)); 290 } 291 boolean doDesignations = false; 292 for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { 293 scanForDesignations(c, langs, designations); 294 } 295 scanForProperties(vs.getExpansion(), langs, properties); 296 if (doInactive) { 297 tr.td().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_INACTIVE)); 298 } 299 if (doDefinition) { 300 tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_DEFINITION)); 301 doDesignations = false; 302 for (String n : Utilities.sorted(properties.keySet())) { 303 tr.td().b().ah(context.prefixLocalHref(properties.get(n))).addText(n); 304 } 305 } else { 306 for (String n : Utilities.sorted(properties.keySet())) { 307 tr.td().b().ah(context.prefixLocalHref(properties.get(n))).addText(n); 308 } 309 // if we're not doing definitions and we don't have too many languages, we'll do them in line 310 doDesignations = langs.size() + properties.size() + designations.size() < MAX_DESIGNATIONS_IN_LINE; 311 312 if (doDesignations) { 313 if (vs.hasLanguage()) { 314 tdDisp.tx(" - "+describeLang(vs.getLanguage())); 315 } 316 for (String url : designations.keySet()) { 317 tr.td().b().addText(designations.get(url)); 318 } 319 for (String lang : langs) { 320 tr.td().b().addText(describeLang(lang)); 321 } 322 } 323 } 324 325 326 addMapHeaders(tr, maps); 327 for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { 328 addExpansionRowToTable(t, vs, c, 1, doLevel, doDefinition, doInactive, maps, langs, designations, doDesignations, properties, res); 329 } 330 331 // now, build observed languages 332 333 if (!doDesignations && langs.size() + designations.size() > 0) { 334 Collections.sort(langs); 335 if (designations.size() == 0) { 336 x.para().b().tx(context.formatPhrase(RenderingContext.GENERAL_ADD_LANG)); 337 } else if (langs.size() == 0) { 338 x.para().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_DESIG)); 339 } else { 340 x.para().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_ADD_DESIG)); 341 } 342 t = x.table("codes", false); 343 tr = t.tr(); 344 tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); 345 for (String url : designations.keySet()) { 346 tr.td().b().addText(designations.get(url)); 347 } 348 for (String lang : langs) { 349 tr.td().b().addText(describeLang(lang)); 350 } 351 for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { 352 addDesignationRow(c, t, langs, designations); 353 } 354 } 355 356 } 357 358 359 private void scanForProperties(ValueSetExpansionComponent exp, List<String> langs, Map<String, String> properties) { 360 properties.clear(); 361 for (ValueSetExpansionPropertyComponent pp : exp.getProperty()) { 362 if (pp.hasCode() && pp.hasUri() && anyActualproperties(exp.getContains(), pp.getCode())) { 363 properties.put(pp.getCode(), pp.getUri()); 364 } 365 } 366 } 367 368 private boolean anyActualproperties(List<ValueSetExpansionContainsComponent> contains, String pp) { 369 for (ValueSetExpansionContainsComponent c : contains) { 370 for (ConceptPropertyComponent cp : c.getProperty()) { 371 if (pp.equals(cp.getCode())) { 372 return true; 373 } 374 } 375 if (anyActualproperties(c.getContains(), pp)) { 376 return true; 377 } 378 } 379 return false; 380 } 381 382 private boolean generateContentModeNotices(XhtmlNode x, ValueSetExpansionComponent expansion, Resource vs) { 383 generateContentModeNotice(x, expansion, "example", context.formatPhrase(RenderingContext.VALUE_SET_EXP), vs); 384 return generateContentModeNotice(x, expansion, "fragment", context.formatPhrase(RenderingContext.VALUE_SET_EXP_FRAG), vs); 385 } 386 387 private boolean generateContentModeNotice(XhtmlNode x, ValueSetExpansionComponent expansion, String mode, String text, Resource vs) { 388 boolean res = false; 389 Multimap<String, String> versions = HashMultimap.create(); 390 for (ValueSetExpansionParameterComponent p : expansion.getParameter()) { 391 if (p.getName().equals(mode)) { 392 String[] parts = ((PrimitiveType) p.getValue()).asStringValue().split("\\|"); 393 if (parts.length == 2 && !Utilities.noString(parts[0])) 394 versions.put(parts[0], parts[1]); 395 } 396 } 397 if (versions.size() > 0) { 398 XhtmlNode div = null; 399 XhtmlNode ul = null; 400 boolean first = true; 401 for (String s : versions.keySet()) { 402 if (versions.size() == 1 && versions.get(s).size() == 1) { 403 for (String v : versions.get(s)) { // though there'll only be one 404 XhtmlNode p = x.para().style("border: black 1px dotted; background-color: #ffcccc; padding: 8px; margin-bottom: 8px"); 405 p.tx(text+" "); 406 expRef(p, s, v, vs); 407 res = true; 408 } 409 } else { 410 for (String v : versions.get(s)) { 411 if (first) { 412 div = x.div().style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px"); 413 div.para().tx(text+"s: "); 414 ul = div.ul(); 415 first = false; 416 res = true; 417 } 418 expRef(ul.li(), s, v, vs); 419 } 420 } 421 } 422 } 423 return res; 424 } 425 426 private boolean checkDoSystem(ValueSet vs, ValueSet src) { 427 if (src != null) 428 vs = src; 429 return vs.hasCompose(); 430 } 431 432 private boolean IsNotFixedExpansion(ValueSet vs) { 433 if (vs.hasCompose()) 434 return false; 435 436 437 // it's not fixed if it has any includes that are not version fixed 438 for (ConceptSetComponent cc : vs.getCompose().getInclude()) { 439 if (cc.hasValueSet()) 440 return true; 441 if (!cc.hasVersion()) 442 return true; 443 } 444 return false; 445 } 446 447 448 449 450 private ConceptMapRenderInstructions findByTarget(DataType source) { 451 if (source == null) { 452 return null; 453 } 454 String src = source.primitiveValue(); 455 if (src == null) { 456 return null; 457 } 458 for (ConceptMapRenderInstructions t : renderingMaps) { 459 if (src.equals(t.getUrl())) 460 return t; 461 } 462 return null; 463 } 464 465 private Integer countMembership(ValueSet vs) { 466 int count = 0; 467 if (vs.hasExpansion()) 468 count = count + ValueSetUtilities.countExpansion(vs); 469 else { 470 if (vs.hasCompose()) { 471 if (vs.getCompose().hasExclude()) { 472 try { 473 ValueSetExpansionOutcome vse = getContext().getWorker().expandVS(vs, true, false); 474 count = 0; 475 count += ValueSetUtilities.countExpansion(vse.getValueset()); 476 return count; 477 } catch (Exception e) { 478 return null; 479 } 480 } 481 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 482 if (inc.hasFilter()) 483 return null; 484 if (!inc.hasConcept()) 485 return null; 486 count = count + inc.getConcept().size(); 487 } 488 } 489 } 490 return count; 491 } 492 493 494 private void addCSRef(XhtmlNode x, String url) { 495 CodeSystem cs = getContext().getWorker().fetchCodeSystem(url); 496 if (cs == null) { 497 x.code(url); 498 } else if (cs.hasWebPath()) { 499 x.ah(context.prefixLocalHref(cs.getWebPath())).tx(cs.present()); 500 } else { 501 x.code(url); 502 x.tx(" ("+cs.present()+")"); 503 } 504 } 505 506 @SuppressWarnings("rawtypes") 507 private void generateVersionNotice(XhtmlNode x, ValueSetExpansionComponent expansion, Resource vs) { 508 509 Multimap<String, String> versions = HashMultimap.create(); 510 Set<String> vlist = new HashSet<>(); 511 for (ValueSetExpansionParameterComponent p : expansion.getParameter()) { 512 if ((p.getName().startsWith("used-") || p.getName().equals("version")) && !vlist.contains(p.getValue().primitiveValue())) { 513 String name = p.getName().equals("version") ? "system" : p.getName().substring(5); 514 vlist.add(p.getValue().primitiveValue()); 515 String[] parts = ((PrimitiveType) p.getValue()).asStringValue().split("\\|"); 516 if (parts.length == 2 && !Utilities.noString(parts[0])) 517 versions.put(name+"|"+parts[0], parts[1]); 518 } 519 } 520 if (versions.size() > 0) { 521 XhtmlNode div = null; 522 XhtmlNode ul = null; 523 boolean first = true; 524 for (String s : Utilities.sorted(versions.keySet())) { 525 if (versions.size() == 1 && versions.get(s).size() == 1) { 526 for (String v : versions.get(s)) { // though there'll only be one 527 XhtmlNode p = x.para().style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px"); 528 if (!vs.hasUserData(UserDataNames.VS_EXPANSION_SOURCE)) { 529 p.tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSION)+" "); 530 } else if ("internal".equals(vs.getUserString(UserDataNames.VS_EXPANSION_SOURCE))) { 531 p.tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSION_INTERNAL)+" "); 532 } else { 533 p.tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSION_SRVR, vs.getUserString(UserDataNames.VS_EXPANSION_SOURCE))+" "); 534 } 535 expRef(p, s, v, vs); 536 } 537 } else { 538 for (String v : versions.get(s)) { 539 if (first) { 540 div = x.div().style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px"); 541 if (!vs.hasUserData(UserDataNames.VS_EXPANSION_SOURCE)) { 542 div.para().tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSIONS)); 543 } else if ("internal".equals(vs.getUserString(UserDataNames.VS_EXPANSION_SOURCE))) { 544 div.para().tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSIONS_INTERNAL)); 545 } else { 546 div.para().tx(context.formatPhrase(RenderingContext.VALUE_SET_EXPANSIONS_SRVR, vs.getUserString(UserDataNames.VS_EXPANSION_SOURCE))); 547 } 548 ul = div.ul(); 549 first = false; 550 } 551 expRef(ul.li(), s, v, vs); 552 } 553 } 554 } 555 } 556 } 557 558 private String findParamValue(List<ValueSetExpansionParameterComponent> list, String name) { 559 for (ValueSetExpansionParameterComponent p : list) { 560 if (name.equals(p.getName())) { 561 return p.getValue().primitiveValue(); 562 } 563 } 564 return null; 565 } 566 567 private void expRef(XhtmlNode x, String u, String v, Resource source) { 568 String t = u.contains("|") ? u.substring(0, u.indexOf("|")) : u; 569 u = u.substring(u.indexOf("|")+1); 570 // TODO Auto-generated method stub 571 if (u.equals("http://snomed.info/sct")) { 572 String[] parts = v.split("\\/"); 573 if (parts.length >= 5) { 574 String m = describeModule(parts[4]); 575 if (parts.length == 7) { 576 x.tx(context.formatPhrase(RenderingContext.VALUE_SET_SNOMED_ADD, m, formatSCTDate(parts[6]))); 577 } else { 578 x.tx(context.formatPhrase(RenderingContext.VALUE_SET_SNOMED, m)); 579 } 580 } else { 581 x.tx(displaySystem(u)+" "+ context.formatPhrase(RenderingContext.GENERAL_VER_LOW) + " " +v); 582 } 583 } else if (u.equals("http://loinc.org")) { 584 String vd = describeLoincVer(v); 585 if (vd != null) { 586 x.tx(context.formatPhrase(RenderingContext.VALUE_SET_LOINCV)+v+" ("+vd+")"); 587 } else { 588 x.tx(context.formatPhrase(RenderingContext.VALUE_SET_LOINCV)+v); 589 } 590 } else if (Utilities.noString(v)) { 591 CanonicalResource cr = (CanonicalResource) getContext().getWorker().fetchResource(Resource.class, u, source); 592 if (cr != null) { 593 if (cr.hasWebPath()) { 594 x.ah(context.prefixLocalHref(cr.getWebPath())).tx(t+" "+cr.present()+" "+ context.formatPhrase(RenderingContext.VALUE_SET_NO_VERSION)+cr.fhirType()+")"); 595 } else { 596 x.tx(t+" "+displaySystem(u)+" "+context.formatPhrase(RenderingContext.VALUE_SET_NO_VERSION)+cr.fhirType()+")"); 597 } 598 } else { 599 x.tx(t+" "+displaySystem(u)+" "+ context.formatPhrase(RenderingContext.VALUE_SET_NO_VER)); 600 } 601 } else { 602 CanonicalResource cr = (CanonicalResource) getContext().getWorker().fetchResource(Resource.class, u+"|"+v, source); 603 if (cr != null) { 604 if (cr.hasWebPath()) { 605 x.ah(context.prefixLocalHref(cr.getWebPath())).tx(t+" "+cr.present()+" v"+v+" ("+cr.fhirType()+")"); 606 } else { 607 x.tx(t+" "+displaySystem(u)+" v"+v+" ("+cr.fhirType()+")"); 608 } 609 } else { 610 x.tx(t+" "+displaySystem(u)+" "+ context.formatPhrase(RenderingContext.GENERAL_VER_LOW)+v); 611 } 612 } 613 } 614 615 private String describeLoincVer(String v) { 616 if ("2.67".equals(v)) return "Dec 2019"; 617 if ("2.66".equals(v)) return "Jun 2019"; 618 if ("2.65".equals(v)) return "Dec 2018"; 619 if ("2.64".equals(v)) return "Jun 2018"; 620 if ("2.63".equals(v)) return "Dec 2017"; 621 if ("2.61".equals(v)) return "Jun 2017"; 622 if ("2.59".equals(v)) return "Feb 2017"; 623 if ("2.58".equals(v)) return "Dec 2016"; 624 if ("2.56".equals(v)) return "Jun 2016"; 625 if ("2.54".equals(v)) return "Dec 2015"; 626 if ("2.52".equals(v)) return "Jun 2015"; 627 if ("2.50".equals(v)) return "Dec 2014"; 628 if ("2.48".equals(v)) return "Jun 2014"; 629 if ("2.46".equals(v)) return "Dec 2013"; 630 if ("2.44".equals(v)) return "Jun 2013"; 631 if ("2.42".equals(v)) return "Dec 2012"; 632 if ("2.40".equals(v)) return "Jun 2012"; 633 if ("2.38".equals(v)) return "Dec 2011"; 634 if ("2.36".equals(v)) return "Jun 2011"; 635 if ("2.34".equals(v)) return "Dec 2010"; 636 if ("2.32".equals(v)) return "Jun 2010"; 637 if ("2.30".equals(v)) return "Feb 2010"; 638 if ("2.29".equals(v)) return "Dec 2009"; 639 if ("2.27".equals(v)) return "Jul 2009"; 640 if ("2.26".equals(v)) return "Jan 2009"; 641 if ("2.24".equals(v)) return "Jul 2008"; 642 if ("2.22".equals(v)) return "Dec 2007"; 643 if ("2.21".equals(v)) return "Jun 2007"; 644 if ("2.19".equals(v)) return "Dec 2006"; 645 if ("2.17".equals(v)) return "Jun 2006"; 646 if ("2.16".equals(v)) return "Dec 2005"; 647 if ("2.15".equals(v)) return "Jun 2005"; 648 if ("2.14".equals(v)) return "Dec 2004"; 649 if ("2.13".equals(v)) return "Aug 2004"; 650 if ("2.12".equals(v)) return "Feb 2004"; 651 if ("2.10".equals(v)) return "Oct 2003"; 652 if ("2.09".equals(v)) return "May 2003"; 653 if ("2.08 ".equals(v)) return "Sep 2002"; 654 if ("2.07".equals(v)) return "Aug 2002"; 655 if ("2.05".equals(v)) return "Feb 2002"; 656 if ("2.04".equals(v)) return "Jan 2002"; 657 if ("2.03".equals(v)) return "Jul 2001"; 658 if ("2.02".equals(v)) return "May 2001"; 659 if ("2.01".equals(v)) return "Jan 2001"; 660 if ("2.00".equals(v)) return "Jan 2001"; 661 if ("1.0n".equals(v)) return "Feb 2000"; 662 if ("1.0ma".equals(v)) return "Aug 1999"; 663 if ("1.0m".equals(v)) return "Jul 1999"; 664 if ("1.0l".equals(v)) return "Jan 1998"; 665 if ("1.0ja".equals(v)) return "Oct 1997"; 666 return null; 667 } 668 669 private String formatSCTDate(String ds) { 670 SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); 671 Date date; 672 try { 673 date = format.parse(ds); 674 } catch (ParseException e) { 675 return ds; 676 } 677 return new SimpleDateFormat("dd-MMM yyyy").format(date); 678 } 679 680 private String describeModule(String module) { 681 switch (module) { 682 case "900000000000207008" : context.formatPhrase(RenderingContext.VALUE_SET_INT); 683 case "449081005" : context.formatPhrase(RenderingContext.VALUE_SET_SPAN); 684 case "11000221109" : context.formatPhrase(RenderingContext.VALUE_SET_AR); 685 case "32506021000036107" : context.formatPhrase(RenderingContext.VALUE_SET_AUS); 686 case "11000234105" : context.formatPhrase(RenderingContext.VALUE_SET_AT); 687 case "11000172109" : context.formatPhrase(RenderingContext.VALUE_SET_BE); 688 case "20621000087109" : context.formatPhrase(RenderingContext.VALUE_SET_CA_EN); 689 case "20611000087101" : context.formatPhrase(RenderingContext.VALUE_SET_CA_FR); 690 case "554471000005108" : context.formatPhrase(RenderingContext.VALUE_SET_DANISH); 691 case "11000181102 " : context.formatPhrase(RenderingContext.VALUE_SET_EE); 692 case "11000229106" : context.formatPhrase(RenderingContext.VALUE_SET_FI); 693 case "11000274103" : context.formatPhrase(RenderingContext.VALUE_SET_DE); 694 case "1121000189102" : context.formatPhrase(RenderingContext.VALUE_SET_IN); 695 case "11000220105" : context.formatPhrase(RenderingContext.VALUE_SET_IE); 696 case "11000146104" : context.formatPhrase(RenderingContext.VALUE_SET_DUTCH); 697 case "21000210109" : context.formatPhrase(RenderingContext.VALUE_SET_NZ); 698 case "51000202101 " : context.formatPhrase(RenderingContext.VALUE_SET_NO); 699 case "11000267109" : context.formatPhrase(RenderingContext.VALUE_SET_KR); 700 case "900000001000122104" : context.formatPhrase(RenderingContext.VALUE_ES_ES); 701 case "45991000052106" : context.formatPhrase(RenderingContext.VALUE_SET_SWEDISH); 702 case "2011000195101" : context.formatPhrase(RenderingContext.VALUE_SET_CH); 703 case "83821000000107" : context.formatPhrase(RenderingContext.VALUE_SET_UK); 704 case "999000021000000109" : context.formatPhrase(RenderingContext.VALUE_SET_UK_CLIN); 705 case "5631000179106" : context.formatPhrase(RenderingContext.VALUE_SET_UY); 706 case "731000124108" : context.formatPhrase(RenderingContext.VALUE_SET_US); 707 case "5991000124107" : context.formatPhrase(RenderingContext.VALUE_SET_US_ICD10CM); 708 default: 709 return module; 710 } 711 } 712 713 private boolean hasVersionParameter(ValueSetExpansionComponent expansion) { 714 for (ValueSetExpansionParameterComponent p : expansion.getParameter()) { 715 if (p.getName().equals("version")) 716 return true; 717 } 718 return false; 719 } 720 721 private void addDesignationRow(ValueSetExpansionContainsComponent c, XhtmlNode t, List<String> langs, Map<String, String> designations) { 722 XhtmlNode tr = t.tr(); 723 tr.td().addText(c.getCode()); 724 addDesignationsToRow(c, designations, tr); 725 addLangaugesToRow(c, langs, tr); 726 for (ValueSetExpansionContainsComponent cc : c.getContains()) { 727 addDesignationRow(cc, t, langs, designations); 728 } 729 } 730 731 public void addDesignationsToRow(ValueSetExpansionContainsComponent c, Map<String, String> designations, XhtmlNode tr) { 732 for (String url : designations.keySet()) { 733 String d = null; 734 if (d == null) { 735 for (ConceptReferenceDesignationComponent dd : c.getDesignation()) { 736 if (url.equals(getUrlForDesignation(dd))) { 737 d = dd.getValue(); 738 } 739 } 740 } 741 tr.td().addText(d == null ? "" : d); 742 } 743 } 744 745 public void addLangaugesToRow(ValueSetExpansionContainsComponent c, List<String> langs, XhtmlNode tr) { 746 for (String lang : langs) { 747 String d = null; 748 for (Extension ext : c.getExtension()) { 749 if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { 750 String l = ToolingExtensions.readStringExtension(ext, "lang"); 751 if (lang.equals(l)) { 752 d = ToolingExtensions.readStringExtension(ext, "content"); 753 } 754 } 755 } 756 if (d == null) { 757 for (ConceptReferenceDesignationComponent dd : c.getDesignation()) { 758 String l = dd.getLanguage(); 759 if (lang.equals(l)) { 760 d = dd.getValue(); 761 } 762 } 763 } 764 tr.td().addText(d == null ? "" : d); 765 } 766 } 767 768 769 private boolean checkDoDefinition(List<ValueSetExpansionContainsComponent> contains) { 770 for (ValueSetExpansionContainsComponent c : contains) { 771 CodeSystem cs = getContext().getWorker().fetchCodeSystem(c.getSystem()); 772 if (cs != null) { 773 ConceptDefinitionComponent cd = CodeSystemUtilities.getCode(cs, c.getCode()); 774 if (cd != null && cd.hasDefinition()) { 775 return true; 776 } 777 } 778 if (checkDoDefinition(c.getContains())) 779 return true; 780 } 781 return false; 782 } 783 784 private boolean checkDoInactive(List<ValueSetExpansionContainsComponent> contains) { 785 for (ValueSetExpansionContainsComponent c : contains) { 786 if (c.hasInactive()) { 787 return true; 788 } 789 if (checkDoInactive(c.getContains())) 790 return true; 791 } 792 return false; 793 } 794 795 796 private boolean allFromOneSystem(ValueSet vs) { 797 if (vs.getExpansion().getContains().isEmpty()) 798 return false; 799 String system = vs.getExpansion().getContains().get(0).getSystem(); 800 for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) { 801 if (!checkSystemMatches(system, cc)) 802 return false; 803 } 804 return true; 805 } 806 807 private String getCsRef(String system) { 808 CodeSystem cs = getContext().getWorker().fetchCodeSystem(system); 809 return getCsRef(cs); 810 } 811 812 private <T extends Resource> String getCsRef(T cs) { 813 if (cs == null) { 814 return "?cs-n?"; 815 } 816 String ref = cs.getWebPath(); 817 if (ref == null) { 818 ref = cs.getUserString(UserDataNames.render_filename); 819 } 820 return ref == null ? null : ref.replace("\\", "/"); 821 } 822 823 private void scanForDesignations(ValueSetExpansionContainsComponent c, List<String> langs, Map<String, String> designations) { 824 for (Extension ext : c.getExtension()) { 825 if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { 826 String lang = ToolingExtensions.readStringExtension(ext, "lang"); 827 if (!Utilities.noString(lang) && !langs.contains(lang)) { 828 langs.add(lang); 829 } 830 } 831 } 832 for (ConceptReferenceDesignationComponent d : c.getDesignation()) { 833 String lang = d.getLanguage(); 834 if (!Utilities.noString(lang) && !langs.contains(lang)) { 835 langs.add(lang); 836 } else { 837 // can we present this as a designation that we know? 838 String disp = getDisplayForDesignation(d); 839 String url = getUrlForDesignation(d); 840 if (disp == null) { 841 disp = getDisplayForUrl(url); 842 } 843 if (disp != null && !designations.containsKey(url) && url != null) { 844 designations.put(url, disp); 845 } 846 } 847 } 848 for (ValueSetExpansionContainsComponent cc : c.getContains()) { 849 scanForDesignations(cc, langs, designations); 850 } 851 } 852 853 private void scanForLangs(ValueSetExpansionContainsComponent c, List<String> langs) { 854 for (Extension ext : c.getExtension()) { 855 if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { 856 String lang = ToolingExtensions.readStringExtension(ext, "lang"); 857 if (!Utilities.noString(lang) && !langs.contains(lang)) { 858 langs.add(lang); 859 } 860 } 861 } 862 for (ConceptReferenceDesignationComponent d : c.getDesignation()) { 863 String lang = d.getLanguage(); 864 if (!Utilities.noString(lang) && !langs.contains(lang)) { 865 langs.add(lang); 866 } 867 } 868 for (ValueSetExpansionContainsComponent cc : c.getContains()) { 869 scanForLangs(cc, langs); 870 } 871 } 872 873 private void addExpansionRowToTable(XhtmlNode t, ValueSet vs, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doDefinition, boolean doInactive, List<UsedConceptMap> maps, List<String> langs, Map<String, String> designations, boolean doDesignations, Map<String, String> properties, ResourceWrapper res) throws FHIRFormatError, DefinitionException, IOException { 874 XhtmlNode tr = t.tr(); 875 if (ValueSetUtilities.isDeprecated(vs, c)) { 876 tr.setAttribute("style", "background-color: #ffeeee"); 877 } 878 879 XhtmlNode td = tr.td(); 880 881 String tgt = makeAnchor(c.getSystem(), c.getCode()); 882 String pfx = res.getScopedId(); 883 td.an((context.prefixAnchor(pfx == null ? "" : pfx+"-")+tgt)); 884 885 if (doLevel) { 886 td.addText(Integer.toString(i)); 887 td = tr.td(); 888 } 889 String s = Utilities.padLeft("", '\u00A0', i*2); 890 td.attribute("style", "white-space:nowrap").addText(s); 891 addCodeToTable(c.getAbstract(), c.getSystem(), c.getVersion(), c.getCode(), c.getDisplay(), td); 892 td = tr.td(); 893 td.addText(c.getSystem()); 894 td = tr.td(); 895 if (c.hasDisplayElement()) 896 td.addText(c.getDisplay()); 897 898 if (doInactive) { 899 td = tr.td(); 900 if (c.getInactive()) { 901 td.tx(context.formatPhrase(RenderingContext.VALUE_SET_INACT)); 902 } 903 } 904 if (doDefinition) { 905 td = tr.td(); 906 CodeSystem cs = getContext().getWorker().fetchCodeSystem(c.getSystem()); 907 if (cs != null) { 908 String defn = CodeSystemUtilities.getCodeDefinition(cs, c.getCode()); 909 addMarkdown(td, defn, cs.getWebPath()); 910 } 911 } 912 for (String n : Utilities.sorted(properties.keySet())) { 913 td = tr.td(); 914 String ps = getPropertyValue(c, n); 915 if (!Utilities.noString(ps)) { 916 td.addText(ps); 917 } 918 } 919 for (UsedConceptMap m : maps) { 920 td = tr.td(); 921 List<TargetElementComponentWrapper> mappings = findMappingsForCode(c.getCode(), m.getMap()); 922 boolean first = true; 923 for (TargetElementComponentWrapper mapping : mappings) { 924 if (!first) 925 td.br(); 926 first = false; 927 XhtmlNode span = td.span(null, mapping.comp.getRelationship().toString()); 928 span.addText(getCharForRelationship(mapping.comp)); 929 addRefToCode(td, mapping.group.getTarget(), null, m.getLink(), mapping.comp.getCode()); 930 if (!Utilities.noString(mapping.comp.getComment())) 931 td.i().tx("("+mapping.comp.getComment()+")"); 932 } 933 } 934 if (doDesignations) { 935 addDesignationsToRow(c, designations, tr); 936 addLangaugesToRow(c, langs, tr); 937 } 938 for (ValueSetExpansionContainsComponent cc : c.getContains()) { 939 addExpansionRowToTable(t, vs, cc, i+1, doLevel, doDefinition, doInactive, maps, langs, designations, doDesignations, properties, res); 940 } 941 } 942 943 944 945 946 947 private String getPropertyValue(ValueSetExpansionContainsComponent c, String n) { 948 for (ConceptPropertyComponent cp : c.getProperty()) { 949 if (n.equals(cp.getCode())) { 950 return cp.getValue().primitiveValue(); 951 } 952 } 953 return null; 954 } 955 956 private boolean checkSystemMatches(String system, ValueSetExpansionContainsComponent cc) { 957 if (!system.equals(cc.getSystem())) 958 return false; 959 for (ValueSetExpansionContainsComponent cc1 : cc.getContains()) { 960 if (!checkSystemMatches(system, cc1)) 961 return false; 962 } 963 return true; 964 } 965 966 private void addCodeToTable(boolean isAbstract, String system, String version, String code, String display, XhtmlNode td) { 967 CodeSystem e = getContext().getWorker().fetchCodeSystem(system); 968 if (e == null || (e.getContent() != org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode.COMPLETE && e.getContent() != org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode.FRAGMENT)) { 969 if (isAbstract) 970 td.i().setAttribute("title", context.formatPhrase(RenderingContext.VS_ABSTRACT_CODE_HINT)).addText(code); 971 else if ("http://snomed.info/sct".equals(system)) { 972 td.ah(context.prefixLocalHref(SnomedUtilities.getSctLink(version, code, context.getContext().getExpansionParameters()))).addText(code); 973 } else if ("http://loinc.org".equals(system)) { 974 td.ah(context.prefixLocalHref(LoincLinker.getLinkForCode(code))).addText(code); 975 } else 976 td.addText(code); 977 } else { 978 String href = context.fixReference(getCsRef(e)); 979 if (href == null) { 980 td.code().tx(code); 981 } else { 982 if (href.contains("#")) 983 href = href + "-"+Utilities.nmtokenize(code); 984 else 985 href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(code); 986 if (isAbstract) 987 td.ah(context.prefixLocalHref(href)).setAttribute("title", context.formatPhrase(RenderingContext.VS_ABSTRACT_CODE_HINT)).i().addText(code); 988 else 989 td.ah(context.prefixLocalHref(href)).addText(code); 990 } 991 } 992 } 993 994 private void addRefToCode(XhtmlNode td, String target, String vslink, String code, String version) { 995 addCodeToTable(false, target, version, code, null, td); 996// CodeSystem cs = getContext().getWorker().fetchCodeSystem(target); 997// String cslink = getCsRef(cs); 998// String link = cslink != null ? cslink+"#"+cs.getId()+"-"+code : vslink+"#"+code; 999// if (!Utilities.isAbsoluteUrl(link)) { 1000// link = getContext().getSpecificationLink()+link; 1001// } 1002// XhtmlNode a = td.ah(context.prefixLocalHref(link)); 1003// a.addText(code); 1004 } 1005 1006 private void generateComposition(RenderingStatus status, ResourceWrapper res, XhtmlNode x, ValueSet vs, boolean header, List<UsedConceptMap> maps) throws FHIRException, IOException { 1007 List<String> langs = new ArrayList<String>(); 1008 Map<String, String> designations = new HashMap<>(); // map of url = description, where url is the designation code. Designations that are for languages won't make it into this list 1009 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 1010 scanDesignations(inc, langs, designations); 1011 } 1012 for (ConceptSetComponent inc : vs.getCompose().getExclude()) { 1013 scanDesignations(inc, langs, designations); 1014 } 1015 boolean doDesignations = langs.size() + designations.size() < MAX_DESIGNATIONS_IN_LINE; 1016 1017 if (header) { 1018 XhtmlNode h = x.h2(); 1019 h.addText(vs.present()); 1020 addMarkdown(x, vs.getDescription()); 1021 if (vs.hasCopyrightElement()) 1022 generateCopyright(x, res); 1023 } 1024 int index = 0; 1025 if (vs.getCompose().getInclude().size() == 1 && vs.getCompose().getExclude().size() == 0 && !VersionComparisonAnnotation.hasDeleted(vs.getCompose(), "include", "exclude")) { 1026 genInclude(status, x.ul(), vs.getCompose().getInclude().get(0), "Include", langs, doDesignations, maps, designations, index, vs); 1027 } else { 1028 XhtmlNode p = x.para(); 1029 p.tx(context.formatPhrase(RenderingContext.VALUE_SET_RULES_INC)); 1030 XhtmlNode ul = x.ul(); 1031 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 1032 genInclude(status, ul, inc, context.formatPhrase(RenderingContext.VALUE_SET_INC), langs, doDesignations, maps, designations, index, vs); 1033 index++; 1034 } 1035 for (Base inc : VersionComparisonAnnotation.getDeleted(vs.getCompose(), "include")) { 1036 genInclude(status, ul, (ConceptSetComponent) inc, context.formatPhrase(RenderingContext.VALUE_SET_INC), langs, doDesignations, maps, designations, index, vs); 1037 index++; 1038 } 1039 if (vs.getCompose().hasExclude() || VersionComparisonAnnotation.hasDeleted(vs.getCompose(), "exclude")) { 1040 p = x.para(); 1041 p.tx(context.formatPhrase(RenderingContext.VALUE_SET_RULES_EXC)); 1042 ul = x.ul(); 1043 for (ConceptSetComponent exc : vs.getCompose().getExclude()) { 1044 genInclude(status, ul, exc, context.formatPhrase(RenderingContext.VALUE_SET_EXCL), langs, doDesignations, maps, designations, index, vs); 1045 index++; 1046 } 1047 for (Base inc : VersionComparisonAnnotation.getDeleted(vs.getCompose(), "exclude")) { 1048 genInclude(status, ul, (ConceptSetComponent) inc, context.formatPhrase(RenderingContext.VALUE_SET_EXCL), langs, doDesignations, maps, designations, index, vs); 1049 index++; 1050 } 1051 } 1052 } 1053 1054 // now, build observed languages 1055 1056 if (!doDesignations && langs.size() + designations.size() > 0) { 1057 Collections.sort(langs); 1058 if (designations.size() == 0) { 1059 x.para().b().tx(context.formatPhrase(RenderingContext.GENERAL_ADD_LANG)); 1060 } else if (langs.size() == 0) { 1061 x.para().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_DESIG)); 1062 } else { 1063 x.para().b().tx(context.formatPhrase(RenderingContext.VALUE_SET_ADD_DESIG)); 1064 } 1065 XhtmlNode t = x.table("codes", false); 1066 XhtmlNode tr = t.tr(); 1067 tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); 1068 for (String url : designations.keySet()) { 1069 tr.td().b().addText(designations.get(url)); 1070 } 1071 for (String lang : langs) { 1072 tr.td().b().addText(describeLang(lang)); 1073 } 1074 for (ConceptSetComponent c : vs.getCompose().getInclude()) { 1075 for (ConceptReferenceComponent cc : c.getConcept()) { 1076 addDesignationRow(cc, t, langs, designations); 1077 } 1078 } 1079 } 1080 } 1081 1082 private void renderExpansionRules(XhtmlNode x, ConceptSetComponent inc, int index, Map<String, ConceptDefinitionComponent> definitions) throws FHIRException, IOException { 1083 String s = context.formatPhrase(RenderingContext.VALUE_SET_NOT_DEF); 1084 if (inc.hasExtension(ToolingExtensions.EXT_EXPAND_RULES)) { 1085 String rule = inc.getExtensionString(ToolingExtensions.EXT_EXPAND_RULES); 1086 if (rule != null) { 1087 switch (rule) { 1088 case "all-codes": s = context.formatPhrase(RenderingContext.VALUE_SET_ALL_CODE); 1089 case "ungrouped": s = context.formatPhrase(RenderingContext.VALUE_SET_NOT_FOUND); 1090 case "groups-only": s = context.formatPhrase(RenderingContext.VALUE_SET_CONT_STRUC); 1091 } 1092 } 1093 } 1094 x.br(); 1095 x.tx(s); 1096 HierarchicalTableGenerator gen = new HierarchicalTableGenerator(context, context.getDestDir(), context.isInlineGraphics(), true, "exp"); 1097 TableModel model = gen.new TableModel("exp.h="+index, context.getRules() == GenerationRules.IG_PUBLISHER); 1098 model.setAlternating(true); 1099 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.GENERAL_CODE), context.formatPhrase(RenderingContext.VALUE_SET_CODE_ITEM), null, 0)); 1100 model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatPhrase(RenderingContext.TX_DISPLAY), context.formatPhrase(RenderingContext.VALUE_SET_DISPLAY_ITEM), null, 0)); 1101 1102 for (Extension ext : inc.getExtensionsByUrl(ToolingExtensions.EXT_EXPAND_GROUP)) { 1103 renderExpandGroup(gen, model, ext, inc, definitions); 1104 } 1105 x.br(); 1106 x.tx("table"); 1107 XhtmlNode xn = gen.generate(model, context.getLocalPrefix(), 1, null); 1108 x.addChildNode(xn); 1109 } 1110 1111 private void renderExpandGroup(HierarchicalTableGenerator gen, TableModel model, Extension ext, ConceptSetComponent inc, Map<String, ConceptDefinitionComponent> definitions) { 1112 Row row = gen.new Row(); 1113 model.getRows().add(row); 1114 row.setIcon("icon_entry_blue.png", "entry"); 1115 String code = ext.getExtensionString("code"); 1116 if (code != null) { 1117 row.getCells().add(gen.new Cell(null, null, code, null, null)); 1118 row.getCells().add(gen.new Cell(null, null, getDisplayForCode(inc, code, definitions), null, null)); 1119 } else if (ext.hasId()) { 1120 row.getCells().add(gen.new Cell(null, null, "(#"+ext.getId()+")", null, null)); 1121 row.getCells().add(gen.new Cell(null, null, ext.getExtensionString("display"), null, null)); 1122 } else { 1123 row.getCells().add(gen.new Cell(null, null, null, null, null)); 1124 row.getCells().add(gen.new Cell(null, null, ext.getExtensionString("display"), null, null)); 1125 } 1126 for (Extension member : ext.getExtensionsByUrl("member")) { 1127 Row subRow = gen.new Row(); 1128 row.getSubRows().add(subRow); 1129 subRow.setIcon("icon_entry_blue.png", "entry"); 1130 String mc = member.getValue().primitiveValue(); 1131 // mc might be a reference to another expansion group - we check that first, or to a code in the compose 1132 if (mc.startsWith("#")) { 1133 // it's a reference by id 1134 subRow.getCells().add(gen.new Cell(null, null, "("+mc+")", null, null)); 1135 subRow.getCells().add(gen.new Cell(null, null, "group reference by id", null, null)); 1136 } else { 1137 Extension tgt = findTargetByCode(inc, mc); 1138 if (tgt != null) { 1139 subRow.getCells().add(gen.new Cell(null, null, mc, null, null)); 1140 subRow.getCells().add(gen.new Cell(null, null, "group reference by code", null, null)); 1141 } else { 1142 subRow.getCells().add(gen.new Cell(null, null, mc, null, null)); 1143 subRow.getCells().add(gen.new Cell(null, null, getDisplayForCode(inc, mc, definitions), null, null)); 1144 } 1145 } 1146 } 1147 } 1148 1149 private Extension findTargetByCode(ConceptSetComponent inc, String mc) { 1150 for (Extension ext : inc.getExtensionsByUrl(ToolingExtensions.EXT_EXPAND_GROUP)) { 1151 String code = ext.getExtensionString("code"); 1152 if (mc.equals(code)) { 1153 return ext; 1154 } 1155 } 1156 return null; 1157 } 1158 1159 private String getDisplayForCode(ConceptSetComponent inc, String code, Map<String, ConceptDefinitionComponent> definitions) { 1160 for (ConceptReferenceComponent cc : inc.getConcept()) { 1161 if (code.equals(cc.getCode())) { 1162 if (cc.hasDisplay()) { 1163 return cc.getDisplay(); 1164 } 1165 } 1166 } 1167 if (definitions.containsKey(code)) { 1168 return definitions.get(code).getDisplay(); 1169 } 1170 return null; 1171 } 1172 1173 private void scanDesignations(ConceptSetComponent inc, List<String> langs, Map<String, String> designations) { 1174 for (ConceptReferenceComponent cc : inc.getConcept()) { 1175 for (Extension ext : cc.getExtension()) { 1176 if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { 1177 String lang = ToolingExtensions.readStringExtension(ext, "lang"); 1178 if (!Utilities.noString(lang) && !langs.contains(lang)) { 1179 langs.add(lang); 1180 } 1181 } 1182 } 1183 for (ConceptReferenceDesignationComponent d : cc.getDesignation()) { 1184 String lang = d.getLanguage(); 1185 if (!Utilities.noString(lang) && !langs.contains(lang)) { 1186 langs.add(lang); 1187 } else { 1188 // can we present this as a designation that we know? 1189 String disp = getDisplayForDesignation(d); 1190 String url = getUrlForDesignation(d); 1191 if (disp == null) { 1192 disp = getDisplayForUrl(url); 1193 } 1194 if (disp != null && !designations.containsKey(url)) { 1195 designations.put(url, disp); 1196 } 1197 } 1198 } 1199 } 1200 } 1201 1202 private String getDisplayForUrl(String url) { 1203 if (url == null) { 1204 return null; 1205 } 1206 switch (url) { 1207 case "http://snomed.info/sct#900000000000003001": 1208 return context.formatPhrase(RenderingContext.VALUE_SET_SPEC_NAME); 1209 case "http://snomed.info/sct#900000000000013009": 1210 return context.formatPhrase(RenderingContext.VALUE_SET_SYNONYM); 1211 default: 1212 // As specified in http://www.hl7.org/fhir/valueset-definitions.html#ValueSet.compose.include.concept.designation.use and in http://www.hl7.org/fhir/codesystem-definitions.html#CodeSystem.concept.designation.use the terminology binding is extensible. 1213 return url; 1214 } 1215 } 1216 1217 private String getUrlForDesignation(ConceptReferenceDesignationComponent d) { 1218 if (d.hasUse() && d.getUse().hasSystem() && d.getUse().hasCode()) { 1219 return d.getUse().getSystem()+"#"+d.getUse().getCode(); 1220 } else { 1221 return null; 1222 } 1223 } 1224 1225 private String getDisplayForDesignation(ConceptReferenceDesignationComponent d) { 1226 if (d.hasUse() && d.getUse().hasDisplay()) { 1227 return d.getUse().getDisplay(); 1228 } else { 1229 return null; 1230 } 1231 } 1232 1233 private void genInclude(RenderingStatus status, XhtmlNode ul, ConceptSetComponent inc, String type, List<String> langs, boolean doDesignations, List<UsedConceptMap> maps, Map<String, String> designations, int index, ValueSet vsRes) throws FHIRException, IOException { 1234 XhtmlNode li; 1235 li = ul.li(); 1236 li = renderStatus(inc, li); 1237 1238 Map<String, ConceptDefinitionComponent> definitions = new HashMap<>(); 1239 1240 if (inc.hasSystem()) { 1241 CodeSystem e = getContext().getWorker().fetchCodeSystem(inc.getSystem()); 1242 if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) { 1243 li.addText(type+" "+ context.formatPhrase(RenderingContext.VALUE_SET_ALL_CODES_DEF) + " "); 1244 addCsRef(inc, li, e); 1245 } else { 1246 if (inc.getConcept().size() > 0) { 1247 li.addText(type+" "+ context.formatPhrase(RenderingContext.VALUE_SET_THESE_CODES_DEF) + " "); 1248 addCsRef(inc, li, e); 1249 if (inc.hasVersion()) { 1250 li.addText(" "+ context.formatPhrase(RenderingContext.GENERAL_VER_LOW) + " "); 1251 li.code(inc.getVersion()); 1252 } 1253 1254 // for performance reasons, we do all the fetching in one batch 1255 definitions = getConceptsForCodes(e, inc, vsRes, index); 1256 1257 1258 XhtmlNode t = li.table("none", false); 1259 boolean hasComments = false; 1260 boolean hasDefinition = false; 1261 for (ConceptReferenceComponent c : inc.getConcept()) { 1262 hasComments = hasComments || ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT); 1263 ConceptDefinitionComponent cc = definitions == null ? null : definitions.get(c.getCode()); 1264 hasDefinition = hasDefinition || ((cc != null && cc.hasDefinition()) || ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)); 1265 } 1266 if (hasComments || hasDefinition) { 1267 status.setExtensions(true); 1268 } 1269 addMapHeaders(addTableHeaderRowStandard(t, false, true, hasDefinition, hasComments, false, false, null, langs, designations, doDesignations), maps); 1270 for (ConceptReferenceComponent c : inc.getConcept()) { 1271 renderConcept(inc, langs, doDesignations, maps, designations, definitions, t, hasComments, hasDefinition, c, inc.getVersion()); 1272 } 1273 for (Base b : VersionComparisonAnnotation.getDeleted(inc, "concept" )) { 1274 renderConcept(inc, langs, doDesignations, maps, designations, definitions, t, hasComments, hasDefinition, (ConceptReferenceComponent) b, inc.getVersion()); 1275 } 1276 } 1277 if (inc.getFilter().size() > 0) { 1278 li.addText(type+" "+ context.formatPhrase(RenderingContext.VALUE_SET_CODES_FROM)); 1279 addCsRef(inc, li, e); 1280 li.tx(" "+ context.formatPhrase(RenderingContext.VALUE_SET_WHERE)+" "); 1281 for (int i = 0; i < inc.getFilter().size(); i++) { 1282 ConceptSetFilterComponent f = inc.getFilter().get(i); 1283 if (i > 0) { 1284 if (i == inc.getFilter().size()-1) { 1285 li.tx(" "+ context.formatPhrase(RenderingContext.VALUE_SET_AND)+" "); 1286 } else { 1287 li.tx(context.formatPhrase(RenderingContext.VALUE_SET_COMMA)+" "); 1288 } 1289 } 1290 XhtmlNode wli = renderStatus(f, li); 1291 if (f.getOp() == FilterOperator.EXISTS) { 1292 if (f.getValue().equals("true")) { 1293 wli.tx(f.getProperty()+" "+ context.formatPhrase(RenderingContext.VALUE_SET_EXISTS)); 1294 } else { 1295 wli.tx(f.getProperty()+" "+ context.formatPhrase(RenderingContext.VALUE_SET_DOESNT_EXIST)); 1296 } 1297 } else { 1298 wli.tx(f.getProperty()+" "+describe(f.getOp())+" "); 1299 if (f.getValueElement().hasExtension(ToolingExtensions.EXT_CQF_EXP)) { 1300 Extension expE = f.getValueElement().getExtensionByUrl(ToolingExtensions.EXT_CQF_EXP); 1301 Expression exp = expE.getValueExpression(); 1302 wli.addText("(as calculated by "); 1303 wli.code().tx(exp.getExpression()); 1304 wli.addText(")"); 1305 } else { 1306 if (e != null && codeExistsInValueSet(e, f.getValue())) { 1307 String href = getContext().fixReference(getCsRef(e)); 1308 if (href == null) { 1309 wli.code().tx(f.getValue()); 1310 } else { 1311 if (href.contains("#")) 1312 href = href + "-"+Utilities.nmtokenize(f.getValue()); 1313 else 1314 href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(f.getValue()); 1315 wli.ah(context.prefixLocalHref(href)).addText(f.getValue()); 1316 } 1317 } else if (inc.hasSystem()) { 1318 wli.addText(f.getValue()); 1319 ValidationResult vr = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions(), inc.getSystem(), inc.getVersion(), f.getValue(), null); 1320 if (vr.isOk() && vr.getDisplay() != null) { 1321 wli.tx(" ("+vr.getDisplay()+")"); 1322 } 1323 } else { 1324 wli.addText(f.getValue()); 1325 } 1326 } 1327 String disp = ToolingExtensions.getDisplayHint(f); 1328 if (disp != null) 1329 wli.tx(" ("+disp+")"); 1330 } 1331 } 1332 } 1333 } 1334 if (inc.hasValueSet()) { 1335 li.tx(context.formatPhrase(RenderingContext.VALUE_SET_WHERE_CODES)+" "); 1336 boolean first = true; 1337 for (UriType vs : inc.getValueSet()) { 1338 if (first) 1339 first = false; 1340 else 1341 li.tx(", "); 1342 XhtmlNode wli = renderStatus(vs, li); 1343 AddVsRef(vs.asStringValue(), wli, vsRes); 1344 } 1345 } 1346 if (inc.hasExtension(ToolingExtensions.EXT_EXPAND_RULES) || inc.hasExtension(ToolingExtensions.EXT_EXPAND_GROUP)) { 1347 status.setExtensions(true); 1348 renderExpansionRules(li, inc, index, definitions); 1349 } 1350 } else { 1351 li.tx(context.formatMessagePlural(inc.getValueSet().size(), RenderingContext.VALUE_SET_IMPORT)+" "); 1352 if (inc.getValueSet().size() <= 2) { 1353 int i = 0; 1354 for (UriType vs : inc.getValueSet()) { 1355 if (i > 0) { 1356 if ( i < inc.getValueSet().size() - 1) { 1357 li.tx(", "); 1358 } else { 1359 li.tx(" and "); 1360 } 1361 } 1362 i++; 1363 XhtmlNode wli = renderStatus(vs, li); 1364 AddVsRef(vs.asStringValue(), wli, vsRes); 1365 } 1366 } else { 1367 XhtmlNode xul = li.ul(); 1368 for (UriType vs : inc.getValueSet()) { 1369 XhtmlNode wli = renderStatus(vs, xul.li()); 1370 AddVsRef(vs.asStringValue(), wli, vsRes); 1371 } 1372 1373 } 1374 } 1375 } 1376 1377 private void renderConcept(ConceptSetComponent inc, List<String> langs, boolean doDesignations, 1378 List<UsedConceptMap> maps, Map<String, String> designations, Map<String, ConceptDefinitionComponent> definitions, 1379 XhtmlNode t, boolean hasComments, boolean hasDefinition, ConceptReferenceComponent c, String version) { 1380 XhtmlNode tr = t.tr(); 1381 XhtmlNode td = renderStatusRow(c, t, tr); 1382 ConceptDefinitionComponent cc = definitions == null ? null : definitions.get(c.getCode()); 1383 addCodeToTable(false, inc.getSystem(), version, c.getCode(), c.hasDisplay()? c.getDisplay() : cc != null ? cc.getDisplay() : "", td); 1384 1385 td = tr.td(); 1386 if (!Utilities.noString(c.getDisplay())) 1387 renderStatus(c.getDisplayElement(), td).addText(c.getDisplay()); 1388 else if (VersionComparisonAnnotation.hasDeleted(c, "display")) { 1389 StringType d = (StringType) VersionComparisonAnnotation.getDeletedItem(c, "display"); 1390 renderStatus(d, td).addText(d.primitiveValue()); 1391 } else if (cc != null && !Utilities.noString(cc.getDisplay())) 1392 td.style("color: #cccccc").addText(cc.getDisplay()); 1393 1394 if (hasDefinition) { 1395 td = tr.td(); 1396 if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)) { 1397 smartAddText(td, ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_DEFINITION)); 1398 } else if (cc != null && !Utilities.noString(cc.getDefinition())) { 1399 smartAddText(td, cc.getDefinition()); 1400 } 1401 } 1402 if (hasComments) { 1403 td = tr.td(); 1404 if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT)) { 1405 smartAddText(td, context.formatPhrase(RenderingContext.VALUE_SET_NOTE, ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_VS_COMMENT)+" ")); 1406 } 1407 } 1408 if (doDesignations) { 1409 addDesignationsToRow(c, designations, tr); 1410 addLangaugesToRow(c, langs, tr); 1411 } 1412 for (UsedConceptMap m : maps) { 1413 td = tr.td(); 1414 List<TargetElementComponentWrapper> mappings = findMappingsForCode(c.getCode(), m.getMap()); 1415 boolean first = true; 1416 for (TargetElementComponentWrapper mapping : mappings) { 1417 if (!first) 1418 td.br(); 1419 first = false; 1420 XhtmlNode span = td.span(null, mapping.comp.getRelationship().toString()); 1421 span.addText(getCharForRelationship(mapping.comp)); 1422 addRefToCode(td, mapping.group.getTarget(), m.getLink(), mapping.comp.getCode(), version); 1423 if (!Utilities.noString(mapping.comp.getComment())) 1424 td.i().tx("("+mapping.comp.getComment()+")"); 1425 } 1426 } 1427 } 1428 1429 public void addDesignationsToRow(ConceptReferenceComponent c, Map<String, String> designations, XhtmlNode tr) { 1430 for (String url : designations.keySet()) { 1431 String d = null; 1432 if (d == null) { 1433 for (ConceptReferenceDesignationComponent dd : c.getDesignation()) { 1434 if (url.equals(getUrlForDesignation(dd))) { 1435 d = dd.getValue(); 1436 } 1437 } 1438 } 1439 tr.td().addText(d == null ? "" : d); 1440 } 1441 } 1442 1443 public void addLangaugesToRow(ConceptReferenceComponent c, List<String> langs, XhtmlNode tr) { 1444 for (String lang : langs) { 1445 String d = null; 1446 for (Extension ext : c.getExtension()) { 1447 if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { 1448 String l = ToolingExtensions.readStringExtension(ext, "lang"); 1449 if (lang.equals(l)) { 1450 d = ToolingExtensions.readStringExtension(ext, "content"); 1451 } 1452 } 1453 } 1454 if (d == null) { 1455 for (ConceptReferenceDesignationComponent dd : c.getDesignation()) { 1456 String l = dd.getLanguage(); 1457 if (lang.equals(l)) { 1458 d = dd.getValue(); 1459 } 1460 } 1461 } 1462 tr.td().addText(d == null ? "" : d); 1463 } 1464 } 1465 1466 1467 private Map<String, ConceptDefinitionComponent> getConceptsForCodes(CodeSystem e, ConceptSetComponent inc, ValueSet source, int index) { 1468 if (e == null) { 1469 e = getContext().getWorker().fetchCodeSystem(inc.getSystem()); 1470 } 1471 1472 ValueSetExpansionComponent vse = null; 1473 if (!context.isNoSlowLookup()) { // && !getContext().getWorker().hasCache()) { removed GG 20220107 like what is this trying to do? 1474 try { 1475 1476 ValueSet vs = new ValueSet(); 1477 vs.setUrl(source.getUrl()+"-inc-"+index); 1478 vs.setStatus(PublicationStatus.ACTIVE); 1479 vs.setCompose(new ValueSetComposeComponent()); 1480 vs.getCompose().setInactive(false); 1481 vs.getCompose().getInclude().add(inc); 1482 1483 ValueSetExpansionOutcome vso = getContext().getWorker().expandVS(vs, true, false); 1484 ValueSet valueset = vso.getValueset(); 1485 if (valueset == null) 1486 throw new TerminologyServiceException(context.formatPhrase(RenderingContext.VALUE_SET_ERROR, vso.getError()+" ")); 1487 vse = valueset.getExpansion(); 1488 1489 } catch (Exception e1) { 1490 return null; 1491 } 1492 } 1493 1494 Map<String, ConceptDefinitionComponent> results = new HashMap<>(); 1495 List<CodingValidationRequest> serverList = new ArrayList<>(); 1496 1497 // 1st pass, anything we can resolve internally 1498 for (ConceptReferenceComponent cc : inc.getConcept()) { 1499 String code = cc.getCode(); 1500 ConceptDefinitionComponent v = null; 1501 if (e != null && code != null) { 1502 v = getConceptForCode(e.getConcept(), code); 1503 } 1504 if (v == null && vse != null) { 1505 v = getConceptForCodeFromExpansion(vse.getContains(), code); 1506 } 1507 if (v != null) { 1508 results.put(code, v); 1509 } else { 1510 serverList.add(new CodingValidationRequest(new Coding(inc.getSystem(), code, null))); 1511 } 1512 } 1513 if (!context.isNoSlowLookup() && !serverList.isEmpty()) { 1514 try { 1515 // todo: split this into 10k batches 1516 int i = 0; 1517 while (serverList.size() > i) { 1518 int len = Integer.min(serverList.size(), MAX_BATCH_VALIDATION_SIZE); 1519 List<CodingValidationRequest> list = serverList.subList(i, i+len); 1520 i += len; 1521 getContext().getWorker().validateCodeBatch(getContext().getTerminologyServiceOptions(), list, null); 1522 for (CodingValidationRequest vr : list) { 1523 ConceptDefinitionComponent v = vr.getResult().asConceptDefinition(); 1524 if (v != null) { 1525 results.put(vr.getCoding().getCode(), v); 1526 } 1527 } 1528 } 1529 } catch (Exception e1) { 1530 return null; 1531 } 1532 } 1533 return results; 1534 } 1535 1536 private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> list, String code) { 1537 for (ConceptDefinitionComponent c : list) { 1538 if (code.equals(c.getCode())) 1539 return c; 1540 ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code); 1541 if (v != null) 1542 return v; 1543 } 1544 return null; 1545 } 1546 1547 private ConceptDefinitionComponent getConceptForCodeFromExpansion(List<ValueSetExpansionContainsComponent> list, String code) { 1548 for (ValueSetExpansionContainsComponent c : list) { 1549 if (code.equals(c.getCode())) { 1550 ConceptDefinitionComponent res = new ConceptDefinitionComponent(); 1551 res.setCode(c.getCode()); 1552 res.setDisplay(c.getDisplay()); 1553 return res; 1554 } 1555 ConceptDefinitionComponent v = getConceptForCodeFromExpansion(c.getContains(), code); 1556 if (v != null) 1557 return v; 1558 } 1559 return null; 1560 } 1561 1562 1563 private boolean codeExistsInValueSet(CodeSystem cs, String code) { 1564 for (ConceptDefinitionComponent c : cs.getConcept()) { 1565 if (inConcept(code, c)) 1566 return true; 1567 } 1568 return false; 1569 } 1570 1571 1572 1573 private void addDesignationRow(ConceptReferenceComponent c, XhtmlNode t, List<String> langs, Map<String, String> designations) { 1574 XhtmlNode tr = t.tr(); 1575 tr.td().addText(c.getCode()); 1576 addDesignationsToRow(c, designations, tr); 1577 addLangaugesToRow(c, langs, tr); 1578 } 1579 1580 1581 private String describe(FilterOperator op) { 1582 if (op == null) 1583 return " "+ context.formatPhrase(RenderingContext.VALUE_SET_NULL); 1584 switch (op) { 1585 case EQUAL: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_EQUAL); 1586 case ISA: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_ISA); 1587 case ISNOTA: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_ISNOTA); 1588 case REGEX: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_REGEX); 1589 case NULL: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_NULLS); 1590 case IN: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_IN); 1591 case NOTIN: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_NOTIN); 1592 case DESCENDENTOF: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_DESCENDENTOF); 1593 case EXISTS: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_EXISTS); 1594 case GENERALIZES: return " "+ context.formatPhrase(RenderingContext.VALUE_SET_GENERALIZES); 1595 } 1596 return null; 1597 } 1598 1599 private boolean inConcept(String code, ConceptDefinitionComponent c) { 1600 if (c.hasCodeElement() && c.getCode().equals(code)) 1601 return true; 1602 for (ConceptDefinitionComponent g : c.getConcept()) { 1603 if (inConcept(code, g)) 1604 return true; 1605 } 1606 return false; 1607 } 1608 1609 1610 @Override 1611 protected void genSummaryTableContent(RenderingStatus status, XhtmlNode tbl, CanonicalResource cr) throws IOException { 1612 super.genSummaryTableContent(status, tbl, cr); 1613 1614 ValueSet vs = (ValueSet) cr; 1615 XhtmlNode tr; 1616 1617 if (CodeSystemUtilities.hasOID(vs)) { 1618 tr = tbl.tr(); 1619 tr.td().tx(context.formatPhrase(RenderingContext.GENERAL_OID)+":"); 1620 tr.td().tx(context.formatPhrase(RenderingContext.CODE_SYS_FOR_OID, CodeSystemUtilities.getOID(vs))); 1621 } 1622 } 1623 1624}