reorganize generation code for terminology resources
This commit is contained in:
parent
8d3a0cf18e
commit
4316c81f7e
|
@ -87,6 +87,7 @@ import org.hl7.fhir.r5.model.UriType;
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
|
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
|
||||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||||
|
import org.hl7.fhir.r5.terminologies.TerminologyRenderer;
|
||||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
||||||
import org.hl7.fhir.r5.utils.NarrativeGenerator;
|
import org.hl7.fhir.r5.utils.NarrativeGenerator;
|
||||||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||||
|
@ -5467,7 +5468,7 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
|
|
||||||
private String summarizeCoding(Coding value) {
|
private String summarizeCoding(Coding value) {
|
||||||
String uri = value.getSystem();
|
String uri = value.getSystem();
|
||||||
String system = NarrativeGenerator.describeSystem(uri);
|
String system = TerminologyRenderer.describeSystem(uri);
|
||||||
if (Utilities.isURL(system)) {
|
if (Utilities.isURL(system)) {
|
||||||
if (system.equals("http://cap.org/protocols"))
|
if (system.equals("http://cap.org/protocols"))
|
||||||
system = "CAP Code";
|
system = "CAP Code";
|
||||||
|
|
|
@ -0,0 +1,489 @@
|
||||||
|
package org.hl7.fhir.r5.terminologies;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
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.FHIRFormatError;
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.r5.model.Coding;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap;
|
||||||
|
import org.hl7.fhir.r5.model.Enumeration;
|
||||||
|
import org.hl7.fhir.r5.model.Extension;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent;
|
||||||
|
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.CodeSystemNavigator;
|
||||||
|
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||||
|
import org.hl7.fhir.utilities.MarkDownProcessor;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
|
|
||||||
|
public class CodeSystemRenderer extends TerminologyRenderer {
|
||||||
|
|
||||||
|
|
||||||
|
private List<String> codeSystemPropList = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mode - whether we are rendering for a resource directly, or for an IG
|
||||||
|
* @param context - common services (terminology server, code system cache etc)
|
||||||
|
* @param markdown - markdown processing engine of the correct sort for the version applicable
|
||||||
|
* @param prefix - the path to the base FHIR specification
|
||||||
|
* @param lang - the language to use (null for the default)
|
||||||
|
*/
|
||||||
|
public CodeSystemRenderer(TerminologyRendererMode mode, IWorkerContext context, MarkDownProcessor markdown, String prefix, String lang) {
|
||||||
|
super(mode, context, markdown, prefix, lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getCodeSystemPropList() {
|
||||||
|
return codeSystemPropList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean generate(XhtmlNode x, CodeSystem cs, boolean header) throws FHIRFormatError, DefinitionException, IOException {
|
||||||
|
boolean hasExtensions = false;
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
XhtmlNode h = x.h2();
|
||||||
|
h.addText(cs.hasTitle() ? cs.getTitle() : cs.getName());
|
||||||
|
addMarkdown(x, cs.getDescription());
|
||||||
|
if (cs.hasCopyright())
|
||||||
|
generateCopyright(x, cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateProperties(x, cs);
|
||||||
|
generateFilters(x, cs);
|
||||||
|
List<UsedConceptMap> maps = new ArrayList<UsedConceptMap>();
|
||||||
|
hasExtensions = generateCodeSystemContent(x, cs, hasExtensions, maps);
|
||||||
|
|
||||||
|
return hasExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateFilters(XhtmlNode x, CodeSystem cs) {
|
||||||
|
if (cs.hasFilter()) {
|
||||||
|
x.para().b().tx(context.translator().translate("xhtml-gen-cs", "Filters", lang));
|
||||||
|
XhtmlNode tbl = x.table("grid");
|
||||||
|
XhtmlNode tr = tbl.tr();
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Code", lang));
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Description", lang));
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "operator", lang));
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Value", lang));
|
||||||
|
for (CodeSystemFilterComponent f : cs.getFilter()) {
|
||||||
|
tr = tbl.tr();
|
||||||
|
tr.td().tx(f.getCode());
|
||||||
|
tr.td().tx(f.getDescription());
|
||||||
|
XhtmlNode td = tr.td();
|
||||||
|
for (Enumeration<org.hl7.fhir.r5.model.Enumerations.FilterOperator> t : f.getOperator())
|
||||||
|
td.tx(t.asStringValue()+" ");
|
||||||
|
tr.td().tx(f.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateProperties(XhtmlNode x, CodeSystem cs) {
|
||||||
|
if (cs.hasProperty()) {
|
||||||
|
boolean hasRendered = false;
|
||||||
|
for (PropertyComponent p : cs.getProperty()) {
|
||||||
|
hasRendered = hasRendered || !p.getCode().equals(ToolingExtensions.getPresentation(p, p.getCodeElement()));
|
||||||
|
}
|
||||||
|
|
||||||
|
x.para().b().tx(context.translator().translate("xhtml-gen-cs", "Properties", lang));
|
||||||
|
XhtmlNode tbl = x.table("grid");
|
||||||
|
XhtmlNode tr = tbl.tr();
|
||||||
|
if (hasRendered) {
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Name", lang));
|
||||||
|
}
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Code", lang));
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "URL", lang));
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Description", lang));
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Type", lang));
|
||||||
|
for (PropertyComponent p : cs.getProperty()) {
|
||||||
|
tr = tbl.tr();
|
||||||
|
if (hasRendered) {
|
||||||
|
tr.td().tx(ToolingExtensions.getPresentation(p, p.getCodeElement()));
|
||||||
|
}
|
||||||
|
tr.td().tx(p.getCode());
|
||||||
|
tr.td().tx(p.getUri());
|
||||||
|
tr.td().tx(p.getDescription());
|
||||||
|
tr.td().tx(p.hasType() ? p.getType().toCode() : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean generateCodeSystemContent(XhtmlNode x, CodeSystem cs, boolean hasExtensions, List<UsedConceptMap> maps) throws FHIRFormatError, DefinitionException, IOException {
|
||||||
|
XhtmlNode p = x.para();
|
||||||
|
if (cs.getContent() == CodeSystemContentMode.COMPLETE)
|
||||||
|
p.tx(context.translator().translateAndFormat("xhtml-gen-cs", lang, "This code system %s defines the following codes", cs.getUrl())+":");
|
||||||
|
else if (cs.getContent() == CodeSystemContentMode.EXAMPLE)
|
||||||
|
p.tx(context.translator().translateAndFormat("xhtml-gen-cs", lang, "This code system %s defines many codes, of which the following are some examples", cs.getUrl())+":");
|
||||||
|
else if (cs.getContent() == CodeSystemContentMode.FRAGMENT )
|
||||||
|
p.tx(context.translator().translateAndFormat("xhtml-gen-cs", lang, "This code system %s defines many codes, of which the following are a subset", cs.getUrl())+":");
|
||||||
|
else if (cs.getContent() == CodeSystemContentMode.NOTPRESENT ) {
|
||||||
|
p.tx(context.translator().translateAndFormat("xhtml-gen-cs", lang, "This code system %s defines many codes, but they are not represented here", cs.getUrl()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
XhtmlNode t = x.table( "codes");
|
||||||
|
boolean commentS = false;
|
||||||
|
boolean deprecated = false;
|
||||||
|
boolean display = false;
|
||||||
|
boolean hierarchy = false;
|
||||||
|
boolean version = false;
|
||||||
|
List<PropertyComponent> properties = new ArrayList<>();
|
||||||
|
for (PropertyComponent cp : cs.getProperty()) {
|
||||||
|
if (showPropertyInTable(cp)) {
|
||||||
|
boolean exists = false;
|
||||||
|
for (ConceptDefinitionComponent c : cs.getConcept()) {
|
||||||
|
exists = exists || conceptsHaveProperty(c, cp);
|
||||||
|
}
|
||||||
|
if (exists) {
|
||||||
|
properties.add(cp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ConceptDefinitionComponent c : cs.getConcept()) {
|
||||||
|
commentS = commentS || conceptsHaveComments(c);
|
||||||
|
deprecated = deprecated || conceptsHaveDeprecated(cs, c);
|
||||||
|
display = display || conceptsHaveDisplay(c);
|
||||||
|
version = version || conceptsHaveVersion(c);
|
||||||
|
hierarchy = hierarchy || c.hasConcept();
|
||||||
|
}
|
||||||
|
CodeSystemNavigator csNav = new CodeSystemNavigator(cs);
|
||||||
|
hierarchy = hierarchy || csNav.isRestructure();
|
||||||
|
|
||||||
|
Set<String> langs = new HashSet<>();
|
||||||
|
addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, true, commentS, version, deprecated, properties), maps);
|
||||||
|
for (ConceptDefinitionComponent c : csNav.getConcepts(null)) {
|
||||||
|
hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs) || hasExtensions;
|
||||||
|
}
|
||||||
|
// if (langs.size() > 0) {
|
||||||
|
// Collections.sort(langs);
|
||||||
|
// x.para().b().tx("Additional Language Displays");
|
||||||
|
// t = x.table( "codes");
|
||||||
|
// XhtmlNode tr = t.tr();
|
||||||
|
// tr.td().b().tx("Code");
|
||||||
|
// for (String lang : langs)
|
||||||
|
// tr.td().b().addText(describeLang(lang));
|
||||||
|
// for (ConceptDefinitionComponent c : cs.getConcept()) {
|
||||||
|
// addLanguageRow(c, t, langs);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return hasExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean conceptsHaveProperty(ConceptDefinitionComponent c, PropertyComponent cp) {
|
||||||
|
if (CodeSystemUtilities.hasProperty(c, cp.getCode()))
|
||||||
|
return true;
|
||||||
|
for (ConceptDefinitionComponent g : c.getConcept())
|
||||||
|
if (conceptsHaveProperty(g, cp))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean showPropertyInTable(PropertyComponent cp) {
|
||||||
|
if (cp.hasCode()) {
|
||||||
|
if (cp.hasExtension(ToolingExtensions.EXT_RENDERED_VALUE)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (cp.getCodeElement().hasExtension(ToolingExtensions.EXT_RENDERED_VALUE)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String uri = cp.getUri();
|
||||||
|
String code = null;
|
||||||
|
if (Utilities.noString(uri)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (uri.contains("#")) {
|
||||||
|
code = uri.substring(uri.indexOf("#")+1);
|
||||||
|
uri = uri.substring(0, uri.indexOf("#"));
|
||||||
|
}
|
||||||
|
if (Utilities.existsInList(uri, "http://hl7.org/fhir/concept-properties") || codeSystemPropList.contains(uri)) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
CodeSystem cs = context.fetchCodeSystem(uri);
|
||||||
|
if (cs == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return CodeSystemUtilities.hasCode(cs, code);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int countConcepts(List<ConceptDefinitionComponent> list) {
|
||||||
|
int count = list.size();
|
||||||
|
for (ConceptDefinitionComponent c : list)
|
||||||
|
if (c.hasConcept())
|
||||||
|
count = count + countConcepts(c.getConcept());
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean conceptsHaveComments(ConceptDefinitionComponent c) {
|
||||||
|
if (ToolingExtensions.hasCSComment(c))
|
||||||
|
return true;
|
||||||
|
for (ConceptDefinitionComponent g : c.getConcept())
|
||||||
|
if (conceptsHaveComments(g))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean conceptsHaveDisplay(ConceptDefinitionComponent c) {
|
||||||
|
if (c.hasDisplay())
|
||||||
|
return true;
|
||||||
|
for (ConceptDefinitionComponent g : c.getConcept())
|
||||||
|
if (conceptsHaveDisplay(g))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean conceptsHaveVersion(ConceptDefinitionComponent c) {
|
||||||
|
if (c.hasUserData("cs.version.notes"))
|
||||||
|
return true;
|
||||||
|
for (ConceptDefinitionComponent g : c.getConcept())
|
||||||
|
if (conceptsHaveVersion(g))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean conceptsHaveDeprecated(CodeSystem cs, ConceptDefinitionComponent c) {
|
||||||
|
if (CodeSystemUtilities.isDeprecated(cs, c))
|
||||||
|
return true;
|
||||||
|
for (ConceptDefinitionComponent g : c.getConcept())
|
||||||
|
if (conceptsHaveDeprecated(cs, g))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, boolean comment, boolean version, boolean deprecated, List<UsedConceptMap> maps, String system, CodeSystem cs, List<PropertyComponent> properties, CodeSystemNavigator csNav, Set<String> langs) throws FHIRFormatError, DefinitionException, IOException {
|
||||||
|
boolean hasExtensions = false;
|
||||||
|
XhtmlNode tr = t.tr();
|
||||||
|
XhtmlNode td = tr.td();
|
||||||
|
if (hasHierarchy) {
|
||||||
|
td.addText(Integer.toString(level+1));
|
||||||
|
td = tr.td();
|
||||||
|
String s = Utilities.padLeft("", '\u00A0', level*2);
|
||||||
|
td.addText(s);
|
||||||
|
}
|
||||||
|
td.attribute("style", "white-space:nowrap").addText(c.getCode());
|
||||||
|
XhtmlNode a;
|
||||||
|
if (c.hasCodeElement()) {
|
||||||
|
td.an(cs.getId()+"-" + Utilities.nmtokenize(c.getCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDisplay) {
|
||||||
|
td = tr.td();
|
||||||
|
renderDisplayName(c, cs, td);
|
||||||
|
}
|
||||||
|
td = tr.td();
|
||||||
|
if (c != null &&
|
||||||
|
c.hasDefinitionElement()) {
|
||||||
|
if (lang == null) {
|
||||||
|
if (hasMarkdownInDefinitions(cs))
|
||||||
|
addMarkdown(td, c.getDefinition());
|
||||||
|
else
|
||||||
|
td.addText(c.getDefinition());
|
||||||
|
} else if (lang.equals("*")) {
|
||||||
|
boolean sl = false;
|
||||||
|
for (ConceptDefinitionDesignationComponent cd : c.getDesignation())
|
||||||
|
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue()))
|
||||||
|
sl = true;
|
||||||
|
td.addText((sl ? cs.getLanguage("en")+": " : "")+c.getDefinition());
|
||||||
|
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
|
||||||
|
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) {
|
||||||
|
td.br();
|
||||||
|
td.addText(cd.getLanguage()+": "+cd.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (lang.equals(cs.getLanguage()) || (lang.equals("en") && !cs.hasLanguage())) {
|
||||||
|
td.addText(c.getDefinition());
|
||||||
|
} else {
|
||||||
|
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
|
||||||
|
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && cd.getLanguage().equals(lang)) {
|
||||||
|
td.addText(cd.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deprecated) {
|
||||||
|
td = tr.td();
|
||||||
|
Boolean b = CodeSystemUtilities.isDeprecated(cs, c);
|
||||||
|
if (b != null && b) {
|
||||||
|
smartAddText(td, context.translator().translate("xhtml-gen-cs", "Deprecated", lang));
|
||||||
|
hasExtensions = true;
|
||||||
|
if (ToolingExtensions.hasExtension(c, ToolingExtensions.EXT_REPLACED_BY)) {
|
||||||
|
Coding cc = (Coding) ToolingExtensions.getExtension(c, ToolingExtensions.EXT_REPLACED_BY).getValue();
|
||||||
|
td.tx(" (replaced by ");
|
||||||
|
String url = getCodingReference(cc, system);
|
||||||
|
if (url != null) {
|
||||||
|
td.ah(url).addText(cc.getCode());
|
||||||
|
td.tx(": "+cc.getDisplay()+")");
|
||||||
|
} else
|
||||||
|
td.addText(cc.getCode()+" '"+cc.getDisplay()+"' in "+cc.getSystem()+")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (comment) {
|
||||||
|
td = tr.td();
|
||||||
|
Extension ext = c.getExtensionByUrl(ToolingExtensions.EXT_CS_COMMENT);
|
||||||
|
if (ext != null) {
|
||||||
|
hasExtensions = true;
|
||||||
|
String bc = ext.hasValue() ? ext.getValue().primitiveValue() : null;
|
||||||
|
Map<String, String> translations = ToolingExtensions.getLanguageTranslations(ext.getValue());
|
||||||
|
|
||||||
|
if (lang == null) {
|
||||||
|
if (bc != null)
|
||||||
|
td.addText(bc);
|
||||||
|
} else if (lang.equals("*")) {
|
||||||
|
boolean sl = false;
|
||||||
|
for (String l : translations.keySet())
|
||||||
|
if (bc == null || !bc.equalsIgnoreCase(translations.get(l)))
|
||||||
|
sl = true;
|
||||||
|
if (bc != null) {
|
||||||
|
td.addText((sl ? cs.getLanguage("en")+": " : "")+bc);
|
||||||
|
}
|
||||||
|
for (String l : translations.keySet()) {
|
||||||
|
if (bc == null || !bc.equalsIgnoreCase(translations.get(l))) {
|
||||||
|
if (!td.getChildNodes().isEmpty())
|
||||||
|
td.br();
|
||||||
|
td.addText(l+": "+translations.get(l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (lang.equals(cs.getLanguage()) || (lang.equals("en") && !cs.hasLanguage())) {
|
||||||
|
if (bc != null)
|
||||||
|
td.addText(bc);
|
||||||
|
} else {
|
||||||
|
if (bc != null)
|
||||||
|
translations.put(cs.getLanguage("en"), bc);
|
||||||
|
for (String l : translations.keySet()) {
|
||||||
|
if (l.equals(lang)) {
|
||||||
|
td.addText(translations.get(l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (version) {
|
||||||
|
td = tr.td();
|
||||||
|
if (c.hasUserData("cs.version.notes"))
|
||||||
|
td.addText(c.getUserString("cs.version.notes"));
|
||||||
|
}
|
||||||
|
if (properties != null) {
|
||||||
|
for (PropertyComponent pc : properties) {
|
||||||
|
td = tr.td();
|
||||||
|
boolean first = true;
|
||||||
|
ConceptPropertyComponent pcv = CodeSystemUtilities.getProperty(c, pc.getCode());
|
||||||
|
if (pcv != null && pcv.hasValue()) {
|
||||||
|
if (first) first = false; else td.addText(", ");
|
||||||
|
if (pcv.hasValueCoding()) {
|
||||||
|
td.addText(pcv.getValueCoding().getCode());
|
||||||
|
} else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrl(pcv.getValue().primitiveValue())) {
|
||||||
|
td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue());
|
||||||
|
} else {
|
||||||
|
td.addText(pcv.getValue().primitiveValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UsedConceptMap m : maps) {
|
||||||
|
td = tr.td();
|
||||||
|
List<TargetElementComponentWrapper> 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.hasRelationship() ? mapping.comp.getRelationship().toCode() : "");
|
||||||
|
span.addText(getCharForRelationship(mapping.comp));
|
||||||
|
a = td.ah(prefix+m.getLink()+"#"+makeAnchor(mapping.group.getTarget(), mapping.comp.getCode()));
|
||||||
|
a.addText(mapping.comp.getCode());
|
||||||
|
if (!Utilities.noString(mapping.comp.getComment()))
|
||||||
|
td.i().tx("("+mapping.comp.getComment()+")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<ConceptDefinitionComponent> ocl = csNav.getOtherChildren(c);
|
||||||
|
for (ConceptDefinitionComponent cc : csNav.getConcepts(c)) {
|
||||||
|
hasExtensions = addDefineRowToTable(t, cc, level+1, hasHierarchy, hasDisplay, comment, version, deprecated, maps, system, cs, properties, csNav, langs) || hasExtensions;
|
||||||
|
}
|
||||||
|
for (ConceptDefinitionComponent cc : ocl) {
|
||||||
|
tr = t.tr();
|
||||||
|
td = tr.td();
|
||||||
|
td.addText(Integer.toString(level+2));
|
||||||
|
td = tr.td();
|
||||||
|
String s = Utilities.padLeft("", '\u00A0', (level+1)*2);
|
||||||
|
td.addText(s);
|
||||||
|
td.attribute("style", "white-space:nowrap");
|
||||||
|
a = td.ah("#"+cs.getId()+"-" + Utilities.nmtokenize(cc.getCode()));
|
||||||
|
a.addText(cc.getCode());
|
||||||
|
if (hasDisplay) {
|
||||||
|
td = tr.td();
|
||||||
|
renderDisplayName(cc, cs, td);
|
||||||
|
}
|
||||||
|
int w = 1 + (deprecated ? 1 : 0) + (comment ? 1 : 0) + (version ? 1 : 0) + maps.size();
|
||||||
|
if (properties != null) {
|
||||||
|
w = w + properties.size();
|
||||||
|
}
|
||||||
|
td = tr.td().colspan(Integer.toString(w));
|
||||||
|
}
|
||||||
|
return hasExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasMarkdownInDefinitions(CodeSystem cs) {
|
||||||
|
return ToolingExtensions.readBoolExtension(cs, "http://hl7.org/fhir/StructureDefinition/codesystem-use-markdown");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void renderDisplayName(ConceptDefinitionComponent c, CodeSystem cs, XhtmlNode td) {
|
||||||
|
if (c.hasDisplayElement()) {
|
||||||
|
if (lang == null) {
|
||||||
|
td.addText(c.getDisplay());
|
||||||
|
} else if (lang.equals("*")) {
|
||||||
|
boolean sl = false;
|
||||||
|
for (ConceptDefinitionDesignationComponent cd : c.getDesignation())
|
||||||
|
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "display") && cd.hasLanguage() && !c.getDisplay().equalsIgnoreCase(cd.getValue()))
|
||||||
|
sl = true;
|
||||||
|
td.addText((sl ? cs.getLanguage("en")+": " : "")+c.getDisplay());
|
||||||
|
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
|
||||||
|
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "display") && cd.hasLanguage() && !c.getDisplay().equalsIgnoreCase(cd.getValue())) {
|
||||||
|
td.br();
|
||||||
|
td.addText(cd.getLanguage()+": "+cd.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (lang.equals(cs.getLanguage()) || (lang.equals("en") && !cs.hasLanguage())) {
|
||||||
|
td.addText(c.getDisplay());
|
||||||
|
} else {
|
||||||
|
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
|
||||||
|
if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "display") && cd.hasLanguage() && cd.getLanguage().equals(lang)) {
|
||||||
|
td.addText(cd.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCodingReference(Coding cc, String system) {
|
||||||
|
if (cc.getSystem().equals(system))
|
||||||
|
return "#"+cc.getCode();
|
||||||
|
if (cc.getSystem().equals("http://snomed.info/sct"))
|
||||||
|
return "http://snomed.info/sct/"+cc.getCode();
|
||||||
|
if (cc.getSystem().equals("http://loinc.org"))
|
||||||
|
return "http://s.details.loinc.org/LOINC/"+cc.getCode()+".html";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,396 @@
|
||||||
|
package org.hl7.fhir.r5.terminologies;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hl7.fhir.exceptions.DefinitionException;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap;
|
||||||
|
import org.hl7.fhir.r5.model.ContactDetail;
|
||||||
|
import org.hl7.fhir.r5.model.ContactPoint;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.OtherElementComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent;
|
||||||
|
import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship;
|
||||||
|
import org.hl7.fhir.r5.model.Narrative.NarrativeStatus;
|
||||||
|
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||||
|
import org.hl7.fhir.r5.utils.NarrativeGenerator.ResourceContext;
|
||||||
|
import org.hl7.fhir.utilities.MarkDownProcessor;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.xhtml.NodeType;
|
||||||
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
|
|
||||||
|
public class ConceptMapRenderer extends TerminologyRenderer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mode - whether we are rendering for a resource directly, or for an IG
|
||||||
|
* @param context - common services (terminology server, code system cache etc)
|
||||||
|
* @param markdown - markdown processing engine of the correct sort for the version applicable
|
||||||
|
* @param prefix - the path to the base FHIR specification
|
||||||
|
* @param lang - the language to use (null for the default)
|
||||||
|
*/
|
||||||
|
public ConceptMapRenderer(TerminologyRendererMode mode, IWorkerContext context, MarkDownProcessor markdown, String prefix, String lang) {
|
||||||
|
super(mode, context, markdown, prefix, lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean generate(ResourceContext rcontext, ConceptMap cm, XhtmlNode x) throws FHIRFormatError, DefinitionException, IOException {
|
||||||
|
x.h2().addText(cm.getName()+" ("+cm.getUrl()+")");
|
||||||
|
|
||||||
|
XhtmlNode p = x.para();
|
||||||
|
p.tx("Mapping from ");
|
||||||
|
if (cm.hasSource())
|
||||||
|
AddVsRef(rcontext, cm.getSource().primitiveValue(), p);
|
||||||
|
else
|
||||||
|
p.tx("(not specified)");
|
||||||
|
p.tx(" to ");
|
||||||
|
if (cm.hasTarget())
|
||||||
|
AddVsRef(rcontext, cm.getTarget().primitiveValue(), p);
|
||||||
|
else
|
||||||
|
p.tx("(not specified)");
|
||||||
|
|
||||||
|
p = x.para();
|
||||||
|
if (cm.getExperimental())
|
||||||
|
p.addText(Utilities.capitalize(cm.getStatus().toString())+" (not intended for production usage). ");
|
||||||
|
else
|
||||||
|
p.addText(Utilities.capitalize(cm.getStatus().toString())+". ");
|
||||||
|
p.tx("Published on "+(cm.hasDate() ? cm.getDateElement().toHumanDisplay() : "?ngen-10?")+" by "+cm.getPublisher());
|
||||||
|
if (!cm.getContact().isEmpty()) {
|
||||||
|
p.tx(" (");
|
||||||
|
boolean firsti = true;
|
||||||
|
for (ContactDetail ci : cm.getContact()) {
|
||||||
|
if (firsti)
|
||||||
|
firsti = false;
|
||||||
|
else
|
||||||
|
p.tx(", ");
|
||||||
|
if (ci.hasName())
|
||||||
|
p.addText(ci.getName()+": ");
|
||||||
|
boolean first = true;
|
||||||
|
for (ContactPoint c : ci.getTelecom()) {
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
p.tx(", ");
|
||||||
|
addTelecom(p, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.tx(")");
|
||||||
|
}
|
||||||
|
p.tx(". ");
|
||||||
|
p.addText(cm.getCopyright());
|
||||||
|
if (!Utilities.noString(cm.getDescription()))
|
||||||
|
addMarkdown(x, cm.getDescription());
|
||||||
|
|
||||||
|
x.br();
|
||||||
|
|
||||||
|
CodeSystem cs = context.fetchCodeSystem("http://hl7.org/fhir/concept-map-relationship");
|
||||||
|
if (cs == null)
|
||||||
|
cs = context.fetchCodeSystem("http://hl7.org/fhir/concept-map-equivalence");
|
||||||
|
String eqpath = cs == null ? null : cs.getUserString("path");
|
||||||
|
|
||||||
|
for (ConceptMapGroupComponent grp : cm.getGroup()) {
|
||||||
|
String src = grp.getSource();
|
||||||
|
boolean comment = false;
|
||||||
|
boolean ok = true;
|
||||||
|
Map<String, HashSet<String>> sources = new HashMap<String, HashSet<String>>();
|
||||||
|
Map<String, HashSet<String>> targets = new HashMap<String, HashSet<String>>();
|
||||||
|
sources.put("code", new HashSet<String>());
|
||||||
|
targets.put("code", new HashSet<String>());
|
||||||
|
SourceElementComponent cc = grp.getElement().get(0);
|
||||||
|
String dst = grp.getTarget();
|
||||||
|
sources.get("code").add(grp.getSource());
|
||||||
|
targets.get("code").add(grp.getTarget());
|
||||||
|
for (SourceElementComponent ccl : grp.getElement()) {
|
||||||
|
ok = ok && ccl.getTarget().size() == 1 && ccl.getTarget().get(0).getDependsOn().isEmpty() && ccl.getTarget().get(0).getProduct().isEmpty();
|
||||||
|
for (TargetElementComponent ccm : ccl.getTarget()) {
|
||||||
|
comment = comment || !Utilities.noString(ccm.getComment());
|
||||||
|
for (OtherElementComponent d : ccm.getDependsOn()) {
|
||||||
|
if (!sources.containsKey(d.getProperty()))
|
||||||
|
sources.put(d.getProperty(), new HashSet<String>());
|
||||||
|
sources.get(d.getProperty()).add(d.getSystem());
|
||||||
|
}
|
||||||
|
for (OtherElementComponent d : ccm.getProduct()) {
|
||||||
|
if (!targets.containsKey(d.getProperty()))
|
||||||
|
targets.put(d.getProperty(), new HashSet<String>());
|
||||||
|
targets.get(d.getProperty()).add(d.getSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String display;
|
||||||
|
if (ok) {
|
||||||
|
// simple
|
||||||
|
XhtmlNode tbl = x.table( "grid");
|
||||||
|
XhtmlNode tr = tbl.tr();
|
||||||
|
tr.td().b().tx("Source Code");
|
||||||
|
tr.td().b().tx("Relationship");
|
||||||
|
tr.td().b().tx("Destination Code");
|
||||||
|
if (comment)
|
||||||
|
tr.td().b().tx("Comment");
|
||||||
|
for (SourceElementComponent ccl : grp.getElement()) {
|
||||||
|
tr = tbl.tr();
|
||||||
|
XhtmlNode td = tr.td();
|
||||||
|
td.addText(ccl.getCode());
|
||||||
|
display = getDisplayForConcept(grp.getSource(), ccl.getCode());
|
||||||
|
if (display != null && !isSameCodeAndDisplay(ccl.getCode(), display))
|
||||||
|
td.tx(" ("+display+")");
|
||||||
|
TargetElementComponent ccm = ccl.getTarget().get(0);
|
||||||
|
if (!ccm.hasRelationship())
|
||||||
|
tr.td().tx(":"+"("+ConceptMapRelationship.EQUIVALENT.toCode()+")");
|
||||||
|
else {
|
||||||
|
if (ccm.getRelationshipElement().hasExtension(ToolingExtensions.EXT_OLD_CONCEPTMAP_EQUIVALENCE)) {
|
||||||
|
String code = ToolingExtensions.readStringExtension(ccm.getRelationshipElement(), ToolingExtensions.EXT_OLD_CONCEPTMAP_EQUIVALENCE);
|
||||||
|
tr.td().ah(eqpath+"#"+code).tx(presentEquivalenceCode(code));
|
||||||
|
} else {
|
||||||
|
tr.td().ah(eqpath+"#"+ccm.getRelationship().toCode()).tx(presentRelationshipCode(ccm.getRelationship().toCode()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td = tr.td();
|
||||||
|
td.addText(ccm.getCode());
|
||||||
|
display = getDisplayForConcept(grp.getTarget(), ccm.getCode());
|
||||||
|
if (display != null && !isSameCodeAndDisplay(ccm.getCode(), display))
|
||||||
|
td.tx(" ("+display+")");
|
||||||
|
if (comment)
|
||||||
|
tr.td().addText(ccm.getComment());
|
||||||
|
addUnmapped(tbl, grp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
XhtmlNode tbl = x.table( "grid");
|
||||||
|
XhtmlNode tr = tbl.tr();
|
||||||
|
XhtmlNode td;
|
||||||
|
tr.td().colspan(Integer.toString(1+sources.size())).b().tx("Source Concept Details");
|
||||||
|
tr.td().b().tx("Relationship");
|
||||||
|
tr.td().colspan(Integer.toString(1+targets.size())).b().tx("Destination Concept Details");
|
||||||
|
if (comment) {
|
||||||
|
tr.td().b().tx("Comment");
|
||||||
|
}
|
||||||
|
tr = tbl.tr();
|
||||||
|
if (sources.get("code").size() == 1) {
|
||||||
|
String url = sources.get("code").iterator().next();
|
||||||
|
renderCSDetailsLink(tr, url, true);
|
||||||
|
} else
|
||||||
|
tr.td().b().tx("Code");
|
||||||
|
for (String s : sources.keySet()) {
|
||||||
|
if (!s.equals("code")) {
|
||||||
|
if (sources.get(s).size() == 1) {
|
||||||
|
String url = sources.get(s).iterator().next();
|
||||||
|
renderCSDetailsLink(tr, url, false);
|
||||||
|
} else
|
||||||
|
tr.td().b().addText(getDescForConcept(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr.td();
|
||||||
|
if (targets.get("code").size() == 1) {
|
||||||
|
String url = targets.get("code").iterator().next();
|
||||||
|
renderCSDetailsLink(tr, url, true);
|
||||||
|
} else
|
||||||
|
tr.td().b().tx("Code");
|
||||||
|
for (String s : targets.keySet()) {
|
||||||
|
if (!s.equals("code")) {
|
||||||
|
if (targets.get(s).size() == 1) {
|
||||||
|
String url = targets.get(s).iterator().next();
|
||||||
|
renderCSDetailsLink(tr, url, false);
|
||||||
|
} else
|
||||||
|
tr.td().b().addText(getDescForConcept(s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (comment)
|
||||||
|
tr.td();
|
||||||
|
|
||||||
|
for (int si = 0; si < grp.getElement().size(); si++) {
|
||||||
|
SourceElementComponent ccl = grp.getElement().get(si);
|
||||||
|
boolean slast = si == grp.getElement().size()-1;
|
||||||
|
boolean first = true;
|
||||||
|
if (ccl.hasNoMap() && ccl.getNoMap()) {
|
||||||
|
tr = tbl.tr();
|
||||||
|
td = tr.td().style("border-right-width: 0px");
|
||||||
|
if (!first)
|
||||||
|
td.style("border-top-style: none");
|
||||||
|
else
|
||||||
|
td.style("border-bottom-style: none");
|
||||||
|
if (sources.get("code").size() == 1)
|
||||||
|
td.addText(ccl.getCode());
|
||||||
|
else
|
||||||
|
td.addText(grp.getSource()+" / "+ccl.getCode());
|
||||||
|
display = getDisplayForConcept(grp.getSource(), ccl.getCode());
|
||||||
|
tr.td().style("border-left-width: 0px").tx(display == null ? "" : display);
|
||||||
|
tr.td().colspan("4").style("background-color: #efefef").tx("(not mapped)");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (int ti = 0; ti < ccl.getTarget().size(); ti++) {
|
||||||
|
TargetElementComponent ccm = ccl.getTarget().get(ti);
|
||||||
|
boolean last = ti == ccl.getTarget().size()-1;
|
||||||
|
tr = tbl.tr();
|
||||||
|
td = tr.td().style("border-right-width: 0px");
|
||||||
|
if (!first && !last)
|
||||||
|
td.style("border-top-style: none; border-bottom-style: none");
|
||||||
|
else if (!first)
|
||||||
|
td.style("border-top-style: none");
|
||||||
|
else if (!last)
|
||||||
|
td.style("border-bottom-style: none");
|
||||||
|
if (first) {
|
||||||
|
if (sources.get("code").size() == 1)
|
||||||
|
td.addText(ccl.getCode());
|
||||||
|
else
|
||||||
|
td.addText(grp.getSource()+" / "+ccl.getCode());
|
||||||
|
display = getDisplayForConcept(grp.getSource(), ccl.getCode());
|
||||||
|
tr.td().style("border-left-width: 0px").tx(display == null ? "" : display);
|
||||||
|
}
|
||||||
|
for (String s : sources.keySet()) {
|
||||||
|
if (!s.equals("code")) {
|
||||||
|
td = tr.td();
|
||||||
|
if (first) {
|
||||||
|
td.addText(getValue(ccm.getDependsOn(), s, sources.get(s).size() != 1));
|
||||||
|
display = getDisplay(ccm.getDependsOn(), s);
|
||||||
|
if (display != null)
|
||||||
|
td.tx(" ("+display+")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
if (!ccm.hasRelationship())
|
||||||
|
tr.td().tx(":"+"("+ConceptMapRelationship.EQUIVALENT.toCode()+")");
|
||||||
|
else {
|
||||||
|
if (ccm.getRelationshipElement().hasExtension(ToolingExtensions.EXT_OLD_CONCEPTMAP_EQUIVALENCE)) {
|
||||||
|
String code = ToolingExtensions.readStringExtension(ccm.getRelationshipElement(), ToolingExtensions.EXT_OLD_CONCEPTMAP_EQUIVALENCE);
|
||||||
|
tr.td().ah(eqpath+"#"+code).tx(presentEquivalenceCode(code));
|
||||||
|
} else {
|
||||||
|
tr.td().ah(eqpath+"#"+ccm.getRelationship().toCode()).tx(presentRelationshipCode(ccm.getRelationship().toCode()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td = tr.td().style("border-right-width: 0px");
|
||||||
|
if (targets.get("code").size() == 1)
|
||||||
|
td.addText(ccm.getCode());
|
||||||
|
else
|
||||||
|
td.addText(grp.getTarget()+" / "+ccm.getCode());
|
||||||
|
display = getDisplayForConcept(grp.getTarget(), ccm.getCode());
|
||||||
|
tr.td().style("border-left-width: 0px").tx(display == null ? "" : display);
|
||||||
|
|
||||||
|
for (String s : targets.keySet()) {
|
||||||
|
if (!s.equals("code")) {
|
||||||
|
td = tr.td();
|
||||||
|
td.addText(getValue(ccm.getProduct(), s, targets.get(s).size() != 1));
|
||||||
|
display = getDisplay(ccm.getProduct(), s);
|
||||||
|
if (display != null)
|
||||||
|
td.tx(" ("+display+")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (comment)
|
||||||
|
tr.td().addText(ccm.getComment());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addUnmapped(tbl, grp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSameCodeAndDisplay(String code, String display) {
|
||||||
|
String c = code.replace(" ", "").replace("-", "").toLowerCase();
|
||||||
|
String d = display.replace(" ", "").replace("-", "").toLowerCase();
|
||||||
|
return c.equals(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String presentRelationshipCode(String code) {
|
||||||
|
if ("related-to".equals(code)) {
|
||||||
|
return "is related to";
|
||||||
|
} else if ("equivalent".equals(code)) {
|
||||||
|
return "is equivalent to";
|
||||||
|
} else if ("broader".equals(code)) {
|
||||||
|
return "maps to wider concept";
|
||||||
|
} else if ("narrower".equals(code)) {
|
||||||
|
return "maps to narrower concept";
|
||||||
|
} else if ("not-related-to".equals(code)) {
|
||||||
|
return "is not related to";
|
||||||
|
} else {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String presentEquivalenceCode(String code) {
|
||||||
|
if ("relatedto".equals(code)) {
|
||||||
|
return "is related to";
|
||||||
|
} else if ("equivalent".equals(code)) {
|
||||||
|
return "is equivalent to";
|
||||||
|
} else if ("equal".equals(code)) {
|
||||||
|
return "is equal to";
|
||||||
|
} else if ("wider".equals(code)) {
|
||||||
|
return "maps to wider concept";
|
||||||
|
} else if ("subsumes".equals(code)) {
|
||||||
|
return "is subsumed by";
|
||||||
|
} else if ("narrower".equals(code)) {
|
||||||
|
return "maps to narrower concept";
|
||||||
|
} else if ("specializes".equals(code)) {
|
||||||
|
return "has specialization";
|
||||||
|
} else if ("inexact".equals(code)) {
|
||||||
|
return "maps loosely to";
|
||||||
|
} else if ("unmatched".equals(code)) {
|
||||||
|
return "has no match";
|
||||||
|
} else if ("disjoint".equals(code)) {
|
||||||
|
return "is not related to";
|
||||||
|
} else {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderCSDetailsLink(XhtmlNode tr, String url, boolean span2) {
|
||||||
|
CodeSystem cs;
|
||||||
|
XhtmlNode td;
|
||||||
|
cs = context.fetchCodeSystem(url);
|
||||||
|
td = tr.td();
|
||||||
|
if (span2) {
|
||||||
|
td.colspan("2");
|
||||||
|
}
|
||||||
|
td.b().tx("Code");
|
||||||
|
td.tx(" from ");
|
||||||
|
if (cs == null)
|
||||||
|
td.tx(url);
|
||||||
|
else
|
||||||
|
td.ah(cs.getUserString("path")).attribute("title", url).tx(cs.present());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addUnmapped(XhtmlNode tbl, ConceptMapGroupComponent grp) {
|
||||||
|
if (grp.hasUnmapped()) {
|
||||||
|
// throw new Error("not done yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDescForConcept(String s) {
|
||||||
|
if (s.startsWith("http://hl7.org/fhir/v2/element/"))
|
||||||
|
return "v2 "+s.substring("http://hl7.org/fhir/v2/element/".length());
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String getValue(List<OtherElementComponent> list, String s, boolean withSystem) {
|
||||||
|
for (OtherElementComponent c : list) {
|
||||||
|
if (s.equals(c.getProperty()))
|
||||||
|
if (withSystem)
|
||||||
|
return c.getSystem()+" / "+c.getValue();
|
||||||
|
else
|
||||||
|
return c.getValue();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDisplay(List<OtherElementComponent> list, String s) {
|
||||||
|
for (OtherElementComponent c : list) {
|
||||||
|
if (s.equals(c.getProperty()))
|
||||||
|
return getDisplayForConcept(c.getSystem(), c.getValue());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,460 @@
|
||||||
|
package org.hl7.fhir.r5.terminologies;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hl7.fhir.exceptions.DefinitionException;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
|
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
|
||||||
|
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap;
|
||||||
|
import org.hl7.fhir.r5.model.ContactPoint;
|
||||||
|
import org.hl7.fhir.r5.model.Questionnaire;
|
||||||
|
import org.hl7.fhir.r5.model.Resource;
|
||||||
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
|
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||||
|
import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem;
|
||||||
|
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
|
||||||
|
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||||
|
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
||||||
|
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||||
|
import org.hl7.fhir.r5.utils.NarrativeGenerator.ResourceContext;
|
||||||
|
import org.hl7.fhir.utilities.MarkDownProcessor;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
import org.hl7.fhir.utilities.MarkDownProcessor.Dialect;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||||
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
|
import org.hl7.fhir.utilities.xhtml.XhtmlParser;
|
||||||
|
|
||||||
|
public class TerminologyRenderer {
|
||||||
|
|
||||||
|
public enum TerminologyRendererMode {
|
||||||
|
RESOURCE, IG
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class TargetElementComponentWrapper {
|
||||||
|
protected ConceptMapGroupComponent group;
|
||||||
|
protected TargetElementComponent comp;
|
||||||
|
protected TargetElementComponentWrapper(ConceptMapGroupComponent group, TargetElementComponent comp) {
|
||||||
|
super();
|
||||||
|
this.group = group;
|
||||||
|
this.comp = comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UsedConceptMap {
|
||||||
|
|
||||||
|
private ConceptMapRenderInstructions details;
|
||||||
|
private String link;
|
||||||
|
private ConceptMap map;
|
||||||
|
public UsedConceptMap(ConceptMapRenderInstructions details, String link, ConceptMap map) {
|
||||||
|
super();
|
||||||
|
this.details = details;
|
||||||
|
this.link = link;
|
||||||
|
this.map = map;
|
||||||
|
}
|
||||||
|
public ConceptMapRenderInstructions getDetails() {
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
public ConceptMap getMap() {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
public String getLink() {
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConceptMapRenderInstructions {
|
||||||
|
private String name;
|
||||||
|
private String url;
|
||||||
|
private boolean doDescription;
|
||||||
|
public ConceptMapRenderInstructions(String name, String url, boolean doDescription) {
|
||||||
|
super();
|
||||||
|
this.name = name;
|
||||||
|
this.url = url;
|
||||||
|
this.doDescription = doDescription;
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
public boolean isDoDescription() {
|
||||||
|
return doDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TerminologyRendererMode mode;
|
||||||
|
protected IWorkerContext context;
|
||||||
|
protected MarkDownProcessor markdown;
|
||||||
|
protected String lang;
|
||||||
|
protected String prefix;
|
||||||
|
protected int headerLevelContext;
|
||||||
|
protected ValidationOptions terminologyServiceOptions = new ValidationOptions();
|
||||||
|
protected boolean noSlowLookup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param mode - whether we are rendering for a resource directly, or for an IG
|
||||||
|
* @param context - common services (terminology server, code system cache etc)
|
||||||
|
* @param markdown - markdown processing engine of the correct sort for the version applicable
|
||||||
|
* @param prefix - the path to the base FHIR specification
|
||||||
|
* @param lang - the language to use (null for the default)
|
||||||
|
*/
|
||||||
|
public TerminologyRenderer(TerminologyRendererMode mode, IWorkerContext context, MarkDownProcessor markdown, String prefix, String lang) {
|
||||||
|
super();
|
||||||
|
this.mode = mode;
|
||||||
|
this.context = context;
|
||||||
|
this.markdown = markdown;
|
||||||
|
this.lang = lang;
|
||||||
|
this.prefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TerminologyRendererMode getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeaderLevelContext() {
|
||||||
|
return headerLevelContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeaderLevelContext(int headerLevelContext) {
|
||||||
|
this.headerLevelContext = headerLevelContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationOptions getTerminologyServiceOptions() {
|
||||||
|
return terminologyServiceOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTerminologyServiceOptions(ValidationOptions terminologyServiceOptions) {
|
||||||
|
this.terminologyServiceOptions = terminologyServiceOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isNoSlowLookup() {
|
||||||
|
return noSlowLookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNoSlowLookup(boolean noSlowLookup) {
|
||||||
|
this.noSlowLookup = noSlowLookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addMarkdown(XhtmlNode x, String text) throws FHIRFormatError, IOException, DefinitionException {
|
||||||
|
if (text != null) {
|
||||||
|
// 1. custom FHIR extensions
|
||||||
|
while (text.contains("[[[")) {
|
||||||
|
String left = text.substring(0, text.indexOf("[[["));
|
||||||
|
String link = text.substring(text.indexOf("[[[")+3, text.indexOf("]]]"));
|
||||||
|
String right = text.substring(text.indexOf("]]]")+3);
|
||||||
|
String url = link;
|
||||||
|
String[] parts = link.split("\\#");
|
||||||
|
StructureDefinition p = context.fetchResource(StructureDefinition.class, parts[0]);
|
||||||
|
if (p == null)
|
||||||
|
p = context.fetchTypeDefinition(parts[0]);
|
||||||
|
if (p == null)
|
||||||
|
p = context.fetchResource(StructureDefinition.class, link);
|
||||||
|
if (p != null) {
|
||||||
|
url = p.getUserString("path");
|
||||||
|
if (url == null)
|
||||||
|
url = p.getUserString("filename");
|
||||||
|
} else
|
||||||
|
throw new DefinitionException("Unable to resolve markdown link "+link);
|
||||||
|
|
||||||
|
text = left+"["+link+"]("+url+")"+right;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. markdown
|
||||||
|
String s = markdown.process(Utilities.escapeXml(text), "narrative generator");
|
||||||
|
XhtmlParser p = new XhtmlParser();
|
||||||
|
XhtmlNode m;
|
||||||
|
try {
|
||||||
|
m = p.parse("<div>"+s+"</div>", "div");
|
||||||
|
} catch (org.hl7.fhir.exceptions.FHIRFormatError e) {
|
||||||
|
throw new FHIRFormatError(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
x.getChildNodes().addAll(m.getChildNodes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void generateCopyright(XhtmlNode x, CanonicalResource cs) {
|
||||||
|
XhtmlNode p = x.para();
|
||||||
|
p.b().tx(context.translator().translate("xhtml-gen-cs", "Copyright Statement:", lang));
|
||||||
|
smartAddText(p, " " + cs.getCopyright());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void smartAddText(XhtmlNode p, String text) {
|
||||||
|
if (text == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String[] lines = text.split("\\r\\n");
|
||||||
|
for (int i = 0; i < lines.length; i++) {
|
||||||
|
if (i > 0)
|
||||||
|
p.br();
|
||||||
|
p.addText(lines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addMapHeaders(XhtmlNode tr, List<UsedConceptMap> maps) throws FHIRFormatError, DefinitionException, IOException {
|
||||||
|
for (UsedConceptMap m : maps) {
|
||||||
|
XhtmlNode td = tr.td();
|
||||||
|
XhtmlNode b = td.b();
|
||||||
|
XhtmlNode a = b.ah(prefix+m.getLink());
|
||||||
|
a.addText(m.getDetails().getName());
|
||||||
|
if (m.getDetails().isDoDescription() && m.getMap().hasDescription())
|
||||||
|
addMarkdown(td, m.getMap().getDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getHeader() {
|
||||||
|
int i = 3;
|
||||||
|
while (i <= headerLevelContext)
|
||||||
|
i++;
|
||||||
|
if (i > 6)
|
||||||
|
i = 6;
|
||||||
|
return "h"+Integer.toString(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String describeSystem(String system) {
|
||||||
|
if (system == null)
|
||||||
|
return "[not stated]";
|
||||||
|
if (system.equals("http://loinc.org"))
|
||||||
|
return "LOINC";
|
||||||
|
if (system.startsWith("http://snomed.info"))
|
||||||
|
return "SNOMED CT";
|
||||||
|
if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm"))
|
||||||
|
return "RxNorm";
|
||||||
|
if (system.equals("http://hl7.org/fhir/sid/icd-9"))
|
||||||
|
return "ICD-9";
|
||||||
|
if (system.equals("http://dicom.nema.org/resources/ontology/DCM"))
|
||||||
|
return "DICOM";
|
||||||
|
if (system.equals("http://unitsofmeasure.org"))
|
||||||
|
return "UCUM";
|
||||||
|
|
||||||
|
return system;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected String makeAnchor(String codeSystem, String code) {
|
||||||
|
String s = codeSystem+'-'+code;
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
for (char c : s.toCharArray()) {
|
||||||
|
if (Character.isAlphabetic(c) || Character.isDigit(c) || c == '.')
|
||||||
|
b.append(c);
|
||||||
|
else
|
||||||
|
b.append('-');
|
||||||
|
}
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<TargetElementComponentWrapper> findMappingsForCode(String code, ConceptMap map) {
|
||||||
|
List<TargetElementComponentWrapper> mappings = new ArrayList<TargetElementComponentWrapper>();
|
||||||
|
|
||||||
|
for (ConceptMapGroupComponent g : map.getGroup()) {
|
||||||
|
for (SourceElementComponent c : g.getElement()) {
|
||||||
|
if (c.getCode().equals(code))
|
||||||
|
for (TargetElementComponent cc : c.getTarget())
|
||||||
|
mappings.add(new TargetElementComponentWrapper(g, cc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected String getCharForRelationship(TargetElementComponent mapping) {
|
||||||
|
if (!mapping.hasRelationship())
|
||||||
|
return "";
|
||||||
|
switch (mapping.getRelationship()) {
|
||||||
|
case EQUIVALENT : return "~";
|
||||||
|
case BROADER : return "<";
|
||||||
|
case NARROWER : return ">";
|
||||||
|
case NOTRELATEDTO : return "!=";
|
||||||
|
default: return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T extends Resource> void addCsRef(ConceptSetComponent inc, XhtmlNode li, T cs) {
|
||||||
|
String ref = null;
|
||||||
|
boolean addHtml = true;
|
||||||
|
if (cs != null) {
|
||||||
|
ref = (String) cs.getUserData("external.url");
|
||||||
|
if (Utilities.noString(ref))
|
||||||
|
ref = (String) cs.getUserData("filename");
|
||||||
|
else
|
||||||
|
addHtml = false;
|
||||||
|
if (Utilities.noString(ref))
|
||||||
|
ref = (String) cs.getUserData("path");
|
||||||
|
}
|
||||||
|
String spec = getSpecialReference(inc.getSystem());
|
||||||
|
if (spec != null) {
|
||||||
|
XhtmlNode a = li.ah(spec);
|
||||||
|
a.code(inc.getSystem());
|
||||||
|
} else if (cs != null && ref != null) {
|
||||||
|
if (!Utilities.noString(prefix) && ref.startsWith("http://hl7.org/fhir/"))
|
||||||
|
ref = ref.substring(20)+"/index.html";
|
||||||
|
else if (addHtml && !ref.contains(".html"))
|
||||||
|
ref = ref + ".html";
|
||||||
|
XhtmlNode a = li.ah(prefix+ref.replace("\\", "/"));
|
||||||
|
a.code(inc.getSystem());
|
||||||
|
} else {
|
||||||
|
li.code(inc.getSystem());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getSpecialReference(String system) {
|
||||||
|
if ("http://snomed.info/sct".equals(system))
|
||||||
|
return "http://www.snomed.org/";
|
||||||
|
if (Utilities.existsInList(system, "http://loinc.org", "http://unitsofmeasure.org", "http://www.nlm.nih.gov/research/umls/rxnorm", "http://ncimeta.nci.nih.gov", "http://fdasis.nlm.nih.gov",
|
||||||
|
"http://www.radlex.org", "http://www.whocc.no/atc", "http://dicom.nema.org/resources/ontology/DCM", "http://www.genenames.org", "http://www.ensembl.org", "http://www.ncbi.nlm.nih.gov/nuccore",
|
||||||
|
"http://www.ncbi.nlm.nih.gov/clinvar", "http://sequenceontology.org", "http://www.hgvs.org/mutnomen", "http://www.ncbi.nlm.nih.gov/projects/SNP", "http://cancer.sanger.ac.uk/cancergenome/projects/cosmic",
|
||||||
|
"http://www.lrg-sequence.org", "http://www.omim.org", "http://www.ncbi.nlm.nih.gov/pubmed", "http://www.pharmgkb.org", "http://clinicaltrials.gov", "http://www.ebi.ac.uk/ipd/imgt/hla/"))
|
||||||
|
return system;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected XhtmlNode addTableHeaderRowStandard(XhtmlNode t, boolean hasHierarchy, boolean hasDisplay, boolean definitions, boolean comments, boolean version, boolean deprecated, List<PropertyComponent> properties) {
|
||||||
|
XhtmlNode tr = t.tr();
|
||||||
|
if (hasHierarchy)
|
||||||
|
tr.td().b().tx("Lvl");
|
||||||
|
tr.td().attribute("style", "white-space:nowrap").b().tx(context.translator().translate("xhtml-gen-cs", "Code", lang));
|
||||||
|
if (hasDisplay)
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Display", lang));
|
||||||
|
if (definitions)
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Definition", lang));
|
||||||
|
if (deprecated)
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Deprecated", lang));
|
||||||
|
if (comments)
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Comments", lang));
|
||||||
|
if (version)
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", "Version", lang));
|
||||||
|
if (properties != null) {
|
||||||
|
for (PropertyComponent pc : properties) {
|
||||||
|
String display = ToolingExtensions.getPresentation(pc, pc.getCodeElement());
|
||||||
|
if (display == null || display.equals(pc.getCode()) && pc.hasUri()) {
|
||||||
|
display = getDisplayForProperty(pc.getUri());
|
||||||
|
if (display == null) {
|
||||||
|
display = pc.getCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr.td().b().tx(context.translator().translate("xhtml-gen-cs", display, lang));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected String getDisplayForProperty(String uri) {
|
||||||
|
if (Utilities.noString(uri)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String code = null;
|
||||||
|
if (uri.contains("#")) {
|
||||||
|
code = uri.substring(uri.indexOf("#")+1);
|
||||||
|
uri = uri.substring(0, uri.indexOf("#"));
|
||||||
|
}
|
||||||
|
CodeSystem cs = context.fetchCodeSystem(uri);
|
||||||
|
if (cs == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ConceptDefinitionComponent cc = CodeSystemUtilities.getCode(cs, code);
|
||||||
|
return cc == null ? null : cc.getDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void AddVsRef(ResourceContext rcontext, String value, XhtmlNode li) {
|
||||||
|
Resource res = null;
|
||||||
|
if (rcontext != null) {
|
||||||
|
BundleEntryComponent be = rcontext.resolve(value);
|
||||||
|
if (be != null) {
|
||||||
|
res = be.getResource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (res != null && !(res instanceof CanonicalResource)) {
|
||||||
|
li.addText(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CanonicalResource vs = (CanonicalResource) res;
|
||||||
|
if (vs == null)
|
||||||
|
vs = context.fetchResource(ValueSet.class, value);
|
||||||
|
if (vs == null)
|
||||||
|
vs = context.fetchResource(StructureDefinition.class, value);
|
||||||
|
// if (vs == null)
|
||||||
|
// vs = context.fetchResource(DataElement.class, value);
|
||||||
|
if (vs == null)
|
||||||
|
vs = context.fetchResource(Questionnaire.class, value);
|
||||||
|
if (vs != null) {
|
||||||
|
String ref = (String) vs.getUserData("path");
|
||||||
|
|
||||||
|
ref = adjustForPath(ref);
|
||||||
|
XhtmlNode a = li.ah(ref == null ? "?ngen-11?" : ref.replace("\\", "/"));
|
||||||
|
a.addText(value);
|
||||||
|
} else {
|
||||||
|
CodeSystem cs = context.fetchCodeSystem(value);
|
||||||
|
if (cs != null) {
|
||||||
|
String ref = (String) cs.getUserData("path");
|
||||||
|
ref = adjustForPath(ref);
|
||||||
|
XhtmlNode a = li.ah(ref == null ? "?ngen-12?" : ref.replace("\\", "/"));
|
||||||
|
a.addText(value);
|
||||||
|
} else if (value.equals("http://snomed.info/sct") || value.equals("http://snomed.info/id")) {
|
||||||
|
XhtmlNode a = li.ah(value);
|
||||||
|
a.tx("SNOMED-CT");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (value.startsWith("http://hl7.org") && !Utilities.existsInList(value, "http://hl7.org/fhir/sid/icd-10-us"))
|
||||||
|
System.out.println("Unable to resolve value set "+value);
|
||||||
|
li.addText(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String adjustForPath(String ref) {
|
||||||
|
if (prefix == null)
|
||||||
|
return ref;
|
||||||
|
else
|
||||||
|
return prefix+ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void addTelecom(XhtmlNode p, ContactPoint c) {
|
||||||
|
if (c.getSystem() == ContactPointSystem.PHONE) {
|
||||||
|
p.tx("Phone: "+c.getValue());
|
||||||
|
} else if (c.getSystem() == ContactPointSystem.FAX) {
|
||||||
|
p.tx("Fax: "+c.getValue());
|
||||||
|
} else if (c.getSystem() == ContactPointSystem.EMAIL) {
|
||||||
|
p.ah( "mailto:"+c.getValue()).addText(c.getValue());
|
||||||
|
} else if (c.getSystem() == ContactPointSystem.URL) {
|
||||||
|
if (c.getValue().length() > 30)
|
||||||
|
p.ah(c.getValue()).addText(c.getValue().substring(0, 30)+"...");
|
||||||
|
else
|
||||||
|
p.ah(c.getValue()).addText(c.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected String getDisplayForConcept(String system, String value) {
|
||||||
|
if (value == null || system == null)
|
||||||
|
return null;
|
||||||
|
ValidationResult cl = context.validateCode(terminologyServiceOptions, system, value, null);
|
||||||
|
return cl == null ? null : cl.getDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -102,6 +102,7 @@ import org.hl7.fhir.r5.model.TypeDetails.ProfiledType;
|
||||||
import org.hl7.fhir.r5.model.UriType;
|
import org.hl7.fhir.r5.model.UriType;
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||||
|
import org.hl7.fhir.r5.terminologies.TerminologyRenderer;
|
||||||
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
|
||||||
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
|
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
|
||||||
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
|
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
|
||||||
|
@ -2612,7 +2613,7 @@ public class StructureMapUtilities {
|
||||||
if (Utilities.noString(code))
|
if (Utilities.noString(code))
|
||||||
throw new FHIRException("Describe Transform, but the code is blank");
|
throw new FHIRException("Describe Transform, but the code is blank");
|
||||||
Coding c = buildCoding(uri, code);
|
Coding c = buildCoding(uri, code);
|
||||||
return NarrativeGenerator.describeSystem(c.getSystem())+"#"+c.getCode()+(c.hasDisplay() ? "("+c.getDisplay()+")" : "");
|
return TerminologyRenderer.describeSystem(c.getSystem())+"#"+c.getCode()+(c.hasDisplay() ? "("+c.getDisplay()+")" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -505,4 +505,6 @@ public class I18nConstants {
|
||||||
//public static final String
|
//public static final String
|
||||||
public static final String XHTML_URL_EMPTY = "XHTML_URL_EMPTY";
|
public static final String XHTML_URL_EMPTY = "XHTML_URL_EMPTY";
|
||||||
public static final String XHTML_URL_INVALID_CHARS = "XHTML_URL_INVALID_CHARS";
|
public static final String XHTML_URL_INVALID_CHARS = "XHTML_URL_INVALID_CHARS";
|
||||||
|
public static final String TERMINOLOGY_TX_SYSTEM_HTTPS = "TERMINOLOGY_TX_SYSTEM_HTTPS";
|
||||||
|
public static final String CODESYSTEM_CS_NO_VS_NOTCOMPLETE = "CODESYSTEM_CS_NO_VS_NOTCOMPLETE";
|
||||||
}
|
}
|
|
@ -482,4 +482,7 @@ MEASURE_M_CRITERIA_CQL_NO_ELM = Error in {0}: No compiled version of CQL found
|
||||||
MEASURE_M_CRITERIA_CQL_ELM_NOT_VALID = = Error in {0}: Compiled version of CQL is not valid
|
MEASURE_M_CRITERIA_CQL_ELM_NOT_VALID = = Error in {0}: Compiled version of CQL is not valid
|
||||||
MEASURE_M_CRITERIA_CQL_NOT_FOUND = The function {1} does not exist in the library {0}
|
MEASURE_M_CRITERIA_CQL_NOT_FOUND = The function {1} does not exist in the library {0}
|
||||||
XHTML_URL_EMPTY = URL is empty
|
XHTML_URL_EMPTY = URL is empty
|
||||||
XHTML_URL_INVALID_CHARS = URL contains Invalid Characters ({0})
|
XHTML_URL_INVALID_CHARS = URL contains Invalid Characters ({0})
|
||||||
|
TERMINOLOGY_TX_SYSTEM_HTTPS = The system URL ''{0}'' wrongly starts with https: not http:
|
||||||
|
CODESYSTEM_CS_NO_VS_NOTCOMPLETE = Review the All Codes Value Set - incomplete CodeSystems generally should not have an all codes value set specified
|
||||||
|
|
Loading…
Reference in New Issue