reorganize generation code for terminology resources

This commit is contained in:
Grahame Grieve 2020-04-23 17:31:58 +10:00
parent 8d3a0cf18e
commit 4316c81f7e
9 changed files with 2735 additions and 2277 deletions

View File

@ -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.ValueSetExpansionComponent;
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.utils.NarrativeGenerator;
import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -5467,7 +5468,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private String summarizeCoding(Coding value) {
String uri = value.getSystem();
String system = NarrativeGenerator.describeSystem(uri);
String system = TerminologyRenderer.describeSystem(uri);
if (Utilities.isURL(system)) {
if (system.equals("http://cap.org/protocols"))
system = "CAP Code";

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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.ValueSet;
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.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
@ -2612,7 +2613,7 @@ public class StructureMapUtilities {
if (Utilities.noString(code))
throw new FHIRException("Describe Transform, but the code is blank");
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()+")" : "");
}

View File

@ -505,4 +505,6 @@ public class I18nConstants {
//public static final String
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 TERMINOLOGY_TX_SYSTEM_HTTPS = "TERMINOLOGY_TX_SYSTEM_HTTPS";
public static final String CODESYSTEM_CS_NO_VS_NOTCOMPLETE = "CODESYSTEM_CS_NO_VS_NOTCOMPLETE";
}

View File

@ -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_NOT_FOUND = The function {1} does not exist in the library {0}
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