
org.hl7.fhir.r4b.renderers.ValueSetRenderer Maven / Gradle / Ivy
The newest version!
package org.hl7.fhir.r4b.renderers;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r4b.context.IWorkerContext.CodingValidationRequest;
import org.hl7.fhir.r4b.context.IWorkerContext.ValidationResult;
import org.hl7.fhir.r4b.model.BooleanType;
import org.hl7.fhir.r4b.model.CanonicalResource;
import org.hl7.fhir.r4b.model.CodeSystem;
import org.hl7.fhir.r4b.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4b.model.Coding;
import org.hl7.fhir.r4b.model.ConceptMap;
import org.hl7.fhir.r4b.model.DataType;
import org.hl7.fhir.r4b.model.DomainResource;
import org.hl7.fhir.r4b.model.Enumerations.FilterOperator;
import org.hl7.fhir.r4b.model.Extension;
import org.hl7.fhir.r4b.model.ExtensionHelper;
import org.hl7.fhir.r4b.model.PrimitiveType;
import org.hl7.fhir.r4b.model.Resource;
import org.hl7.fhir.r4b.model.UriType;
import org.hl7.fhir.r4b.model.ValueSet;
import org.hl7.fhir.r4b.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r4b.model.ValueSet.ConceptReferenceDesignationComponent;
import org.hl7.fhir.r4b.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4b.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.r4b.renderers.utils.RenderingContext;
import org.hl7.fhir.r4b.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r4b.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r4b.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r4b.utils.ToolingExtensions;
import org.hl7.fhir.utilities.LoincLinker;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/**
* Rendering framework:
*
* See R5 rendering framework to render R4B resources
*
*/
@Deprecated
public class ValueSetRenderer extends TerminologyRenderer {
public ValueSetRenderer(RenderingContext context) {
super(context);
}
public ValueSetRenderer(RenderingContext context, ResourceContext rcontext) {
super(context, rcontext);
}
private static final String ABSTRACT_CODE_HINT = "This code is not selectable ('Abstract')";
private static final int MAX_LANGS_IN_LINE = 5;
private List renderingMaps = new ArrayList();
public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
return render(x, (ValueSet) dr, false);
}
public boolean render(XhtmlNode x, ValueSet vs, boolean header)
throws FHIRFormatError, DefinitionException, IOException {
List maps = findReleventMaps(vs);
boolean hasExtensions;
if (vs.hasExpansion()) {
// for now, we just accept an expansion if there is one
hasExtensions = generateExpansion(x, vs, header, maps);
} else {
hasExtensions = generateComposition(x, vs, header, maps);
}
return hasExtensions;
}
public void describe(XhtmlNode x, ValueSet vs) {
x.tx(display(vs));
}
public String display(ValueSet vs) {
return vs.present();
}
private List findReleventMaps(ValueSet vs) throws FHIRException {
List res = new ArrayList();
for (CanonicalResource md : getContext().getWorker().allConformanceResources()) {
if (md instanceof ConceptMap) {
ConceptMap cm = (ConceptMap) md;
if (isSource(vs, cm.getSource())) {
ConceptMapRenderInstructions re = findByTarget(cm.getTarget());
if (re != null) {
ValueSet vst = cm.hasTarget() ? getContext().getWorker().fetchResource(ValueSet.class,
cm.hasTargetCanonicalType() ? cm.getTargetCanonicalType().getValue()
: cm.getTargetUriType().asStringValue())
: null;
res.add(new UsedConceptMap(re, vst == null ? cm.getUserString("path") : vst.getUserString("path"), cm));
}
}
}
}
return res;
// Map mymaps = new HashMap();
// for (ConceptMap a : context.getWorker().findMapsForSource(vs.getUrl())) {
// String url = "";
// ValueSet vsr = context.getWorker().fetchResource(ValueSet.class, ((Reference) a.getTarget()).getReference());
// if (vsr != null)
// url = (String) vsr.getUserData("filename");
// mymaps.put(a, url);
// }
// Map mymaps = new HashMap();
// for (ConceptMap a : context.getWorker().findMapsForSource(cs.getValueSet())) {
// String url = "";
// ValueSet vsr = context.getWorker().fetchResource(ValueSet.class, ((Reference) a.getTarget()).getReference());
// if (vsr != null)
// url = (String) vsr.getUserData("filename");
// mymaps.put(a, url);
// }
// also, look in the contained resources for a concept map
// for (Resource r : cs.getContained()) {
// if (r instanceof ConceptMap) {
// ConceptMap cm = (ConceptMap) r;
// if (((Reference) cm.getSource()).getReference().equals(cs.getValueSet())) {
// String url = "";
// ValueSet vsr = context.getWorker().fetchResource(ValueSet.class, ((Reference) cm.getTarget()).getReference());
// if (vsr != null)
// url = (String) vsr.getUserData("filename");
// mymaps.put(cm, url);
// }
// }
// }
}
private boolean isSource(ValueSet vs, DataType source) {
return vs.hasUrl() && source != null && vs.getUrl().equals(source.primitiveValue());
}
private boolean generateExpansion(XhtmlNode x, ValueSet vs, boolean header, List maps)
throws FHIRFormatError, DefinitionException, IOException {
boolean hasExtensions = false;
List langs = new ArrayList();
if (header) {
XhtmlNode h = x.addTag(getHeader());
h.tx("Value Set Contents");
if (IsNotFixedExpansion(vs))
addMarkdown(x, vs.getDescription());
if (vs.hasCopyright())
generateCopyright(x, vs);
}
if (ToolingExtensions.hasExtension(vs.getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY)) {
List exl = vs.getExpansion().getExtensionsByUrl(ToolingExtensions.EXT_EXP_TOOCOSTLY);
boolean other = false;
for (Extension ex : exl) {
if (ex.getValue() instanceof BooleanType) {
x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px")
.addText(vs.getExpansion().getContains().isEmpty() ? getContext().getTooCostlyNoteEmpty()
: getContext().getTooCostlyNoteNotEmpty());
} else if (!other) {
x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px")
.addText(vs.getExpansion().getContains().isEmpty() ? getContext().getTooCostlyNoteEmptyDependent()
: getContext().getTooCostlyNoteNotEmptyDependent());
other = true;
}
}
} else {
Integer count = countMembership(vs);
if (count == null)
x.para().tx("This value set does not contain a fixed number of concepts");
else
x.para().tx("This value set contains " + count.toString() + " concepts");
}
generateContentModeNotices(x, vs.getExpansion());
generateVersionNotice(x, vs.getExpansion());
CodeSystem allCS = null;
boolean doLevel = false;
for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) {
if (cc.hasContains()) {
doLevel = true;
break;
}
}
boolean doSystem = true; // checkDoSystem(vs, src);
boolean doDefinition = checkDoDefinition(vs.getExpansion().getContains());
if (doSystem && allFromOneSystem(vs)) {
doSystem = false;
XhtmlNode p = x.para();
p.tx("All codes in this table are from the system ");
allCS = getContext().getWorker().fetchCodeSystem(vs.getExpansion().getContains().get(0).getSystem());
String ref = null;
if (allCS != null)
ref = getCsRef(allCS);
if (ref == null)
p.code(vs.getExpansion().getContains().get(0).getSystem());
else
p.ah(context.fixReference(ref)).code(vs.getExpansion().getContains().get(0).getSystem());
}
XhtmlNode t = x.table("codes");
XhtmlNode tr = t.tr();
if (doLevel)
tr.td().b().tx("Lvl");
tr.td().attribute("style", "white-space:nowrap").b().tx("Code");
if (doSystem)
tr.td().b().tx("System");
XhtmlNode tdDisp = tr.td();
tdDisp.b().tx("Display");
boolean doLangs = false;
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
scanForLangs(c, langs);
}
if (doDefinition) {
tr.td().b().tx("Definition");
doLangs = false;
} else {
// if we're not doing definitions and we don't have too many languages, we'll do
// them in line
if (langs.size() < MAX_LANGS_IN_LINE) {
doLangs = true;
if (vs.hasLanguage()) {
tdDisp.tx(" - " + describeLang(vs.getLanguage()));
}
for (String lang : langs) {
tr.td().b().addText(describeLang(lang));
}
}
}
addMapHeaders(tr, maps);
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
addExpansionRowToTable(t, c, 0, doLevel, doSystem, doDefinition, maps, allCS, langs, doLangs);
}
// now, build observed languages
if (!doLangs && langs.size() > 0) {
Collections.sort(langs);
x.para().b().tx("Additional Language Displays");
t = x.table("codes");
tr = t.tr();
tr.td().b().tx("Code");
for (String lang : langs) {
tr.td().b().addText(describeLang(lang));
}
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
addLanguageRow(c, t, langs);
}
}
return hasExtensions;
}
private void generateContentModeNotices(XhtmlNode x, ValueSetExpansionComponent expansion) {
generateContentModeNotice(x, expansion, "example", "Expansion based on example code system");
generateContentModeNotice(x, expansion, "fragment", "Expansion based on code system fragment");
}
private void generateContentModeNotice(XhtmlNode x, ValueSetExpansionComponent expansion, String mode, String text) {
Multimap versions = HashMultimap.create();
for (ValueSetExpansionParameterComponent p : expansion.getParameter()) {
if (p.getName().equals(mode)) {
String[] parts = ((PrimitiveType) p.getValue()).asStringValue().split("\\|");
if (parts.length == 2)
versions.put(parts[0], parts[1]);
}
}
if (versions.size() > 0) {
XhtmlNode div = null;
XhtmlNode ul = null;
boolean first = true;
for (String s : versions.keySet()) {
if (versions.size() == 1 && versions.get(s).size() == 1) {
for (String v : versions.get(s)) { // though there'll only be one
XhtmlNode p = x.para()
.style("border: black 1px dotted; background-color: #ffcccc; padding: 8px; margin-bottom: 8px");
p.tx(text + " ");
expRef(p, s, v);
}
} else {
for (String v : versions.get(s)) {
if (first) {
div = x.div()
.style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px");
div.para().tx(text + "s: ");
ul = div.ul();
first = false;
}
expRef(ul.li(), s, v);
}
}
}
}
}
private boolean checkDoSystem(ValueSet vs, ValueSet src) {
if (src != null)
vs = src;
return vs.hasCompose();
}
private boolean IsNotFixedExpansion(ValueSet vs) {
if (vs.hasCompose())
return false;
// it's not fixed if it has any includes that are not version fixed
for (ConceptSetComponent cc : vs.getCompose().getInclude()) {
if (cc.hasValueSet())
return true;
if (!cc.hasVersion())
return true;
}
return false;
}
private ConceptMapRenderInstructions findByTarget(DataType source) {
if (source == null) {
return null;
}
String src = source.primitiveValue();
if (src != null)
for (ConceptMapRenderInstructions t : renderingMaps) {
if (src.equals(t.getUrl()))
return t;
}
return null;
}
private Integer countMembership(ValueSet vs) {
int count = 0;
if (vs.hasExpansion())
count = count + conceptCount(vs.getExpansion().getContains());
else {
if (vs.hasCompose()) {
if (vs.getCompose().hasExclude()) {
try {
ValueSetExpansionOutcome vse = getContext().getWorker().expandVS(vs, true, false);
count = 0;
count += conceptCount(vse.getValueset().getExpansion().getContains());
return count;
} catch (Exception e) {
return null;
}
}
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
if (inc.hasFilter())
return null;
if (!inc.hasConcept())
return null;
count = count + inc.getConcept().size();
}
}
}
return count;
}
private int conceptCount(List list) {
int count = 0;
for (ValueSetExpansionContainsComponent c : list) {
if (!c.getAbstract())
count++;
count = count + conceptCount(c.getContains());
}
return count;
}
private void addCSRef(XhtmlNode x, String url) {
CodeSystem cs = getContext().getWorker().fetchCodeSystem(url);
if (cs == null) {
x.code(url);
} else if (cs.hasUserData("path")) {
x.ah(cs.getUserString("path")).tx(cs.present());
} else {
x.code(url);
x.tx(" (" + cs.present() + ")");
}
}
@SuppressWarnings("rawtypes")
private void generateVersionNotice(XhtmlNode x, ValueSetExpansionComponent expansion) {
Multimap versions = HashMultimap.create();
for (ValueSetExpansionParameterComponent p : expansion.getParameter()) {
if (p.getName().equals("version")) {
String[] parts = ((PrimitiveType) p.getValue()).asStringValue().split("\\|");
if (parts.length == 2)
versions.put(parts[0], parts[1]);
}
}
if (versions.size() > 0) {
XhtmlNode div = null;
XhtmlNode ul = null;
boolean first = true;
for (String s : versions.keySet()) {
if (versions.size() == 1 && versions.get(s).size() == 1) {
for (String v : versions.get(s)) { // though there'll only be one
XhtmlNode p = x.para()
.style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px");
p.tx("Expansion based on ");
expRef(p, s, v);
}
} else {
for (String v : versions.get(s)) {
if (first) {
div = x.div()
.style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px");
div.para().tx("Expansion based on: ");
ul = div.ul();
first = false;
}
expRef(ul.li(), s, v);
}
}
}
}
}
private void expRef(XhtmlNode x, String u, String v) {
// TODO Auto-generated method stub
if (u.equals("http://snomed.info/sct")) {
String[] parts = v.split("\\/");
if (parts.length >= 5) {
String m = describeModule(parts[4]);
if (parts.length == 7) {
x.tx("SNOMED CT " + m + " edition " + formatSCTDate(parts[6]));
} else {
x.tx("SNOMED CT " + m + " edition");
}
} else {
x.tx(describeSystem(u) + " version " + v);
}
} else if (u.equals("http://loinc.org")) {
String vd = describeLoincVer(v);
if (vd != null) {
x.tx("Loinc v" + v + " (" + vd + ")");
} else {
x.tx("Loinc v" + v);
}
} else {
CanonicalResource cr = (CanonicalResource) getContext().getWorker().fetchResource(Resource.class, u + "|" + v);
if (cr != null) {
if (cr.hasUserData("path")) {
x.ah(cr.getUserString("path")).tx(cr.present() + " v" + v + " (" + cr.fhirType() + ")");
} else {
x.tx(describeSystem(u) + " v" + v + " (" + cr.fhirType() + ")");
}
} else {
x.tx(describeSystem(u) + " version " + v);
}
}
}
private String describeLoincVer(String v) {
if ("2.67".equals(v))
return "Dec 2019";
if ("2.66".equals(v))
return "Jun 2019";
if ("2.65".equals(v))
return "Dec 2018";
if ("2.64".equals(v))
return "Jun 2018";
if ("2.63".equals(v))
return "Dec 2017";
if ("2.61".equals(v))
return "Jun 2017";
if ("2.59".equals(v))
return "Feb 2017";
if ("2.58".equals(v))
return "Dec 2016";
if ("2.56".equals(v))
return "Jun 2016";
if ("2.54".equals(v))
return "Dec 2015";
if ("2.52".equals(v))
return "Jun 2015";
if ("2.50".equals(v))
return "Dec 2014";
if ("2.48".equals(v))
return "Jun 2014";
if ("2.46".equals(v))
return "Dec 2013";
if ("2.44".equals(v))
return "Jun 2013";
if ("2.42".equals(v))
return "Dec 2012";
if ("2.40".equals(v))
return "Jun 2012";
if ("2.38".equals(v))
return "Dec 2011";
if ("2.36".equals(v))
return "Jun 2011";
if ("2.34".equals(v))
return "Dec 2010";
if ("2.32".equals(v))
return "Jun 2010";
if ("2.30".equals(v))
return "Feb 2010";
if ("2.29".equals(v))
return "Dec 2009";
if ("2.27".equals(v))
return "Jul 2009";
if ("2.26".equals(v))
return "Jan 2009";
if ("2.24".equals(v))
return "Jul 2008";
if ("2.22".equals(v))
return "Dec 2007";
if ("2.21".equals(v))
return "Jun 2007";
if ("2.19".equals(v))
return "Dec 2006";
if ("2.17".equals(v))
return "Jun 2006";
if ("2.16".equals(v))
return "Dec 2005";
if ("2.15".equals(v))
return "Jun 2005";
if ("2.14".equals(v))
return "Dec 2004";
if ("2.13".equals(v))
return "Aug 2004";
if ("2.12".equals(v))
return "Feb 2004";
if ("2.10".equals(v))
return "Oct 2003";
if ("2.09".equals(v))
return "May 2003";
if ("2.08 ".equals(v))
return "Sep 2002";
if ("2.07".equals(v))
return "Aug 2002";
if ("2.05".equals(v))
return "Feb 2002";
if ("2.04".equals(v))
return "Jan 2002";
if ("2.03".equals(v))
return "Jul 2001";
if ("2.02".equals(v))
return "May 2001";
if ("2.01".equals(v))
return "Jan 2001";
if ("2.00".equals(v))
return "Jan 2001";
if ("1.0n".equals(v))
return "Feb 2000";
if ("1.0ma".equals(v))
return "Aug 1999";
if ("1.0m".equals(v))
return "Jul 1999";
if ("1.0l".equals(v))
return "Jan 1998";
if ("1.0ja".equals(v))
return "Oct 1997";
return null;
}
private String formatSCTDate(String ds) {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
Date date;
try {
date = format.parse(ds);
} catch (ParseException e) {
return ds;
}
return new SimpleDateFormat("dd-MMM yyyy").format(date);
}
private String describeModule(String module) {
if ("900000000000207008".equals(module))
return "International";
if ("731000124108".equals(module))
return "United States";
if ("32506021000036107".equals(module))
return "Australian";
if ("449081005".equals(module))
return "Spanish";
if ("554471000005108".equals(module))
return "Danish";
if ("11000146104".equals(module))
return "Dutch";
if ("45991000052106".equals(module))
return "Swedish";
if ("999000041000000102".equals(module))
return "United Kingdon";
return module;
}
private boolean hasVersionParameter(ValueSetExpansionComponent expansion) {
for (ValueSetExpansionParameterComponent p : expansion.getParameter()) {
if (p.getName().equals("version"))
return true;
}
return false;
}
private void addLanguageRow(ValueSetExpansionContainsComponent c, XhtmlNode t, List langs) {
XhtmlNode tr = t.tr();
tr.td().addText(c.getCode());
addLangaugesToRow(c, langs, tr);
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
addLanguageRow(cc, t, langs);
}
}
public void addLangaugesToRow(ValueSetExpansionContainsComponent c, List langs, XhtmlNode tr) {
for (String lang : langs) {
String d = null;
for (Extension ext : c.getExtension()) {
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
String l = ToolingExtensions.readStringExtension(ext, "lang");
if (lang.equals(l)) {
d = ToolingExtensions.readStringExtension(ext, "content");
}
}
}
if (d == null) {
for (ConceptReferenceDesignationComponent dd : c.getDesignation()) {
String l = dd.getLanguage();
if (lang.equals(l)) {
d = dd.getValue();
}
}
}
tr.td().addText(d == null ? "" : d);
}
}
private boolean checkDoDefinition(List contains) {
for (ValueSetExpansionContainsComponent c : contains) {
CodeSystem cs = getContext().getWorker().fetchCodeSystem(c.getSystem());
if (cs != null)
return true;
if (checkDoDefinition(c.getContains()))
return true;
}
return false;
}
private boolean allFromOneSystem(ValueSet vs) {
if (vs.getExpansion().getContains().isEmpty())
return false;
String system = vs.getExpansion().getContains().get(0).getSystem();
for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) {
if (!checkSystemMatches(system, cc))
return false;
}
return true;
}
private String getCsRef(String system) {
CodeSystem cs = getContext().getWorker().fetchCodeSystem(system);
return getCsRef(cs);
}
private String getCsRef(T cs) {
String ref = (String) cs.getUserData("filename");
if (ref == null)
ref = (String) cs.getUserData("path");
if (ref == null)
return "?ngen-14?.html";
if (!ref.contains(".html"))
ref = ref + ".html";
return ref.replace("\\", "/");
}
private void scanForLangs(ValueSetExpansionContainsComponent c, List langs) {
for (Extension ext : c.getExtension()) {
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
String lang = ToolingExtensions.readStringExtension(ext, "lang");
if (!Utilities.noString(lang) && !langs.contains(lang)) {
langs.add(lang);
}
}
}
for (ConceptReferenceDesignationComponent d : c.getDesignation()) {
String lang = d.getLanguage();
if (!Utilities.noString(lang) && !langs.contains(lang)) {
langs.add(lang);
}
}
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
scanForLangs(cc, langs);
}
}
private void addExpansionRowToTable(XhtmlNode t, ValueSetExpansionContainsComponent c, int i, boolean doLevel,
boolean doSystem, boolean doDefinition, List maps, CodeSystem allCS, List langs,
boolean doLangs) {
XhtmlNode tr = t.tr();
XhtmlNode td = tr.td();
String tgt = makeAnchor(c.getSystem(), c.getCode());
td.an(tgt);
if (doLevel) {
td.addText(Integer.toString(i));
td = tr.td();
}
String s = Utilities.padLeft("", '\u00A0', i * 2);
td.attribute("style", "white-space:nowrap").addText(s);
addCodeToTable(c.getAbstract(), c.getSystem(), c.getCode(), c.getDisplay(), td);
if (doSystem) {
td = tr.td();
td.addText(c.getSystem());
}
td = tr.td();
if (c.hasDisplayElement())
td.addText(c.getDisplay());
if (doDefinition) {
CodeSystem cs = allCS;
if (cs == null)
cs = getContext().getWorker().fetchCodeSystem(c.getSystem());
td = tr.td();
if (cs != null)
td.addText(CodeSystemUtilities.getCodeDefinition(cs, c.getCode()));
}
for (UsedConceptMap m : maps) {
td = tr.td();
List mappings = findMappingsForCode(c.getCode(), m.getMap());
boolean first = true;
for (TargetElementComponentWrapper mapping : mappings) {
if (!first)
td.br();
first = false;
XhtmlNode span = td.span(null, mapping.comp.getEquivalence().toString());
span.addText(getCharForRelationship(mapping.comp));
addRefToCode(td, mapping.group.getTarget(), m.getLink(), mapping.comp.getCode());
if (!Utilities.noString(mapping.comp.getComment()))
td.i().tx("(" + mapping.comp.getComment() + ")");
}
}
if (doLangs) {
addLangaugesToRow(c, langs, tr);
}
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
addExpansionRowToTable(t, cc, i + 1, doLevel, doSystem, doDefinition, maps, allCS, langs, doLangs);
}
}
private boolean checkSystemMatches(String system, ValueSetExpansionContainsComponent cc) {
if (!system.equals(cc.getSystem()))
return false;
for (ValueSetExpansionContainsComponent cc1 : cc.getContains()) {
if (!checkSystemMatches(system, cc1))
return false;
}
return true;
}
private void addCodeToTable(boolean isAbstract, String system, String code, String display, XhtmlNode td) {
CodeSystem e = getContext().getWorker().fetchCodeSystem(system);
if (e == null || e.getContent() != org.hl7.fhir.r4b.model.CodeSystem.CodeSystemContentMode.COMPLETE) {
if (isAbstract)
td.i().setAttribute("title", ABSTRACT_CODE_HINT).addText(code);
else if ("http://snomed.info/sct".equals(system)) {
td.ah(sctLink(code)).addText(code);
} else if ("http://loinc.org".equals(system)) {
td.ah(LoincLinker.getLinkForCode(code)).addText(code);
} else
td.addText(code);
} else {
String href = context.fixReference(getCsRef(e));
if (href.contains("#"))
href = href + "-" + Utilities.nmtokenize(code);
else
href = href + "#" + e.getId() + "-" + Utilities.nmtokenize(code);
if (isAbstract)
td.ah(href).setAttribute("title", ABSTRACT_CODE_HINT).i().addText(code);
else
td.ah(href).addText(code);
}
}
public String sctLink(String code) {
// if (snomedEdition != null)
// http://browser.ihtsdotools.org/?perspective=full&conceptId1=428041000124106&edition=us-edition&release=v20180301&server=https://prod-browser-exten.ihtsdotools.org/api/snomed&langRefset=900000000000509007
return "http://snomed.info/id/" + code;
}
private void addRefToCode(XhtmlNode td, String target, String vslink, String code) {
CodeSystem cs = getContext().getWorker().fetchCodeSystem(target);
String cslink = getCsRef(cs);
XhtmlNode a = null;
if (cslink != null)
a = td.ah(getContext().getSpecificationLink() + cslink + "#" + cs.getId() + "-" + code);
else
a = td.ah(getContext().getSpecificationLink() + vslink + "#" + code);
a.addText(code);
}
private boolean generateComposition(XhtmlNode x, ValueSet vs, boolean header, List maps)
throws FHIRException, IOException {
boolean hasExtensions = false;
List langs = new ArrayList();
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
scanForLangs(inc, langs);
}
for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
scanForLangs(inc, langs);
}
boolean doLangs = langs.size() < MAX_LANGS_IN_LINE;
if (header) {
XhtmlNode h = x.h2();
h.addText(vs.present());
addMarkdown(x, vs.getDescription());
if (vs.hasCopyrightElement())
generateCopyright(x, vs);
}
if (vs.getCompose().getInclude().size() == 1 && vs.getCompose().getExclude().size() == 0) {
hasExtensions = genInclude(x.ul(), vs.getCompose().getInclude().get(0), "Include", langs, doLangs, maps)
|| hasExtensions;
} else {
XhtmlNode p = x.para();
p.tx("This value set includes codes based on the following rules:");
XhtmlNode ul = x.ul();
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
hasExtensions = genInclude(ul, inc, "Include", langs, doLangs, maps) || hasExtensions;
}
if (vs.getCompose().hasExclude()) {
p = x.para();
p.tx("This value set excludes codes based on the following rules:");
ul = x.ul();
for (ConceptSetComponent exc : vs.getCompose().getExclude()) {
hasExtensions = genInclude(ul, exc, "Exclude", langs, doLangs, maps) || hasExtensions;
}
}
}
// now, build observed languages
if (!doLangs && langs.size() > 0) {
Collections.sort(langs);
x.para().b().tx("Additional Language Displays");
XhtmlNode t = x.table("codes");
XhtmlNode tr = t.tr();
tr.td().b().tx("Code");
for (String lang : langs)
tr.td().b().addText(describeLang(lang));
for (ConceptSetComponent c : vs.getCompose().getInclude()) {
for (ConceptReferenceComponent cc : c.getConcept()) {
addLanguageRow(cc, t, langs);
}
}
}
return hasExtensions;
}
private void scanForLangs(ConceptSetComponent inc, List langs) {
for (ConceptReferenceComponent cc : inc.getConcept()) {
for (Extension ext : cc.getExtension()) {
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
String lang = ToolingExtensions.readStringExtension(ext, "lang");
if (!Utilities.noString(lang) && !langs.contains(lang)) {
langs.add(lang);
}
}
}
for (ConceptReferenceDesignationComponent d : cc.getDesignation()) {
String lang = d.getLanguage();
if (!Utilities.noString(lang) && !langs.contains(lang)) {
langs.add(lang);
}
}
}
}
private boolean genInclude(XhtmlNode ul, ConceptSetComponent inc, String type, List langs, boolean doLangs,
List maps) throws FHIRException, IOException {
boolean hasExtensions = false;
XhtmlNode li;
li = ul.li();
CodeSystem e = getContext().getWorker().fetchCodeSystem(inc.getSystem());
if (inc.hasSystem()) {
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
li.addText(type + " all codes defined in ");
addCsRef(inc, li, e);
} else {
if (inc.getConcept().size() > 0) {
li.addText(type + " these codes as defined in ");
addCsRef(inc, li, e);
if (inc.hasVersion()) {
li.addText(" version ");
li.code(inc.getVersion());
}
// for performance reasons, we do all the fetching in one batch
Map definitions = getConceptsForCodes(e, inc);
XhtmlNode t = li.table("none");
boolean hasComments = false;
boolean hasDefinition = false;
for (ConceptReferenceComponent c : inc.getConcept()) {
hasComments = hasComments || ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT);
ConceptDefinitionComponent cc = definitions.get(c.getCode());
hasDefinition = hasDefinition || ((cc != null && cc.hasDefinition())
|| ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION));
}
if (hasComments || hasDefinition)
hasExtensions = true;
addMapHeaders(
addTableHeaderRowStandard(t, false, true, hasDefinition, hasComments, false, false, null, langs, doLangs),
maps);
for (ConceptReferenceComponent c : inc.getConcept()) {
XhtmlNode tr = t.tr();
XhtmlNode td = tr.td();
ConceptDefinitionComponent cc = definitions.get(c.getCode());
addCodeToTable(false, inc.getSystem(), c.getCode(),
c.hasDisplay() ? c.getDisplay() : cc != null ? cc.getDisplay() : "", td);
td = tr.td();
if (!Utilities.noString(c.getDisplay()))
td.addText(c.getDisplay());
else if (cc != null && !Utilities.noString(cc.getDisplay()))
td.addText(cc.getDisplay());
if (hasDefinition) {
td = tr.td();
if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)) {
smartAddText(td, ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_DEFINITION));
} else if (cc != null && !Utilities.noString(cc.getDefinition())) {
smartAddText(td, cc.getDefinition());
}
}
if (hasComments) {
td = tr.td();
if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT)) {
smartAddText(td, "Note: " + ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_VS_COMMENT));
}
}
if (doLangs) {
addLangaugesToRow(c, langs, tr);
}
}
}
if (inc.getFilter().size() > 0) {
li.addText(type + " codes from ");
addCsRef(inc, li, e);
li.tx(" where ");
for (int i = 0; i < inc.getFilter().size(); i++) {
ConceptSetFilterComponent f = inc.getFilter().get(i);
if (i > 0) {
if (i == inc.getFilter().size() - 1) {
li.tx(" and ");
} else {
li.tx(", ");
}
}
if (f.getOp() == FilterOperator.EXISTS) {
if (f.getValue().equals("true")) {
li.tx(f.getProperty() + " exists");
} else {
li.tx(f.getProperty() + " doesn't exist");
}
} else {
li.tx(f.getProperty() + " " + describe(f.getOp()) + " ");
if (e != null && codeExistsInValueSet(e, f.getValue())) {
String href = getContext().fixReference(getCsRef(e));
if (href.contains("#"))
href = href + "-" + Utilities.nmtokenize(f.getValue());
else
href = href + "#" + e.getId() + "-" + Utilities.nmtokenize(f.getValue());
li.ah(href).addText(f.getValue());
} else if ("concept".equals(f.getProperty()) && inc.hasSystem()) {
li.addText(f.getValue());
ValidationResult vr = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions(),
inc.getSystem(), inc.getVersion(), f.getValue(), null);
if (vr.isOk()) {
li.tx(" (" + vr.getDisplay() + ")");
}
} else
li.addText(f.getValue());
String disp = ToolingExtensions.getDisplayHint(f);
if (disp != null)
li.tx(" (" + disp + ")");
}
}
}
}
if (inc.hasValueSet()) {
li.tx(", where the codes are contained in ");
boolean first = true;
for (UriType vs : inc.getValueSet()) {
if (first)
first = false;
else
li.tx(", ");
AddVsRef(vs.asStringValue(), li);
}
}
} else {
li.tx("Import all the codes that are contained in ");
if (inc.getValueSet().size() < 4) {
boolean first = true;
for (UriType vs : inc.getValueSet()) {
if (first)
first = false;
else
li.tx(", ");
AddVsRef(vs.asStringValue(), li);
}
} else {
XhtmlNode xul = li.ul();
for (UriType vs : inc.getValueSet()) {
AddVsRef(vs.asStringValue(), xul.li());
}
}
}
return hasExtensions;
}
public void addLangaugesToRow(ConceptReferenceComponent c, List langs, XhtmlNode tr) {
for (String lang : langs) {
String d = null;
for (Extension ext : c.getExtension()) {
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
String l = ToolingExtensions.readStringExtension(ext, "lang");
if (lang.equals(l)) {
d = ToolingExtensions.readStringExtension(ext, "content");
}
}
}
if (d == null) {
for (ConceptReferenceDesignationComponent dd : c.getDesignation()) {
String l = dd.getLanguage();
if (lang.equals(l)) {
d = dd.getValue();
}
}
}
tr.td().addText(d == null ? "" : d);
}
}
private Map getConceptsForCodes(CodeSystem e, ConceptSetComponent inc) {
if (e == null) {
e = getContext().getWorker().fetchCodeSystem(inc.getSystem());
}
ValueSetExpansionComponent vse = null;
if (!context.isNoSlowLookup() && !getContext().getWorker().hasCache()) {
try {
ValueSetExpansionOutcome vso = getContext().getWorker().expandVS(inc, false);
ValueSet valueset = vso.getValueset();
if (valueset == null)
throw new TerminologyServiceException("Error Expanding ValueSet: " + vso.getError());
vse = valueset.getExpansion();
} catch (TerminologyServiceException e1) {
return null;
}
}
Map results = new HashMap<>();
List serverList = new ArrayList<>();
// 1st pass, anything we can resolve internally
for (ConceptReferenceComponent cc : inc.getConcept()) {
String code = cc.getCode();
ConceptDefinitionComponent v = null;
if (e != null) {
v = getConceptForCode(e.getConcept(), code);
}
if (v == null && vse != null) {
v = getConceptForCodeFromExpansion(vse.getContains(), code);
}
if (v != null) {
results.put(code, v);
} else {
serverList.add(new CodingValidationRequest(new Coding(inc.getSystem(), code, null)));
}
}
if (!context.isNoSlowLookup() && !serverList.isEmpty()) {
getContext().getWorker().validateCodeBatch(getContext().getTerminologyServiceOptions(), serverList, null);
for (CodingValidationRequest vr : serverList) {
ConceptDefinitionComponent v = vr.getResult().asConceptDefinition();
if (v != null) {
results.put(vr.getCoding().getCode(), v);
}
}
}
return results;
}
private ConceptDefinitionComponent getConceptForCode(List list, String code) {
for (ConceptDefinitionComponent c : list) {
if (code.equals(c.getCode()))
return c;
ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code);
if (v != null)
return v;
}
return null;
}
private ConceptDefinitionComponent getConceptForCodeFromExpansion(List list,
String code) {
for (ValueSetExpansionContainsComponent c : list) {
if (code.equals(c.getCode())) {
ConceptDefinitionComponent res = new ConceptDefinitionComponent();
res.setCode(c.getCode());
res.setDisplay(c.getDisplay());
return res;
}
ConceptDefinitionComponent v = getConceptForCodeFromExpansion(c.getContains(), code);
if (v != null)
return v;
}
return null;
}
private boolean codeExistsInValueSet(CodeSystem cs, String code) {
for (ConceptDefinitionComponent c : cs.getConcept()) {
if (inConcept(code, c))
return true;
}
return false;
}
private void addLanguageRow(ConceptReferenceComponent c, XhtmlNode t, List langs) {
XhtmlNode tr = t.tr();
tr.td().addText(c.getCode());
for (String lang : langs) {
String d = null;
for (ConceptReferenceDesignationComponent cd : c.getDesignation()) {
String l = cd.getLanguage();
if (lang.equals(l))
d = cd.getValue();
}
tr.td().addText(d == null ? "" : d);
}
}
private String describe(FilterOperator op) {
if (op == null)
return " null ";
switch (op) {
case EQUAL:
return " = ";
case ISA:
return " is-a ";
case ISNOTA:
return " is-not-a ";
case REGEX:
return " matches (by regex) ";
case NULL:
return " ?ngen-13? ";
case IN:
return " in ";
case NOTIN:
return " not in ";
case DESCENDENTOF:
return " descends from ";
case EXISTS:
return " exists ";
case GENERALIZES:
return " generalizes ";
}
return null;
}
private boolean inConcept(String code, ConceptDefinitionComponent c) {
if (c.hasCodeElement() && c.getCode().equals(code))
return true;
for (ConceptDefinitionComponent g : c.getConcept()) {
if (inConcept(code, g))
return true;
}
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy