Merge branch 'master' of https://github.com/hapifhir/org.hl7.fhir.core into testing_updates
This commit is contained in:
commit
7673400a13
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.hl7.fhir.dstu2</artifactId>
|
||||
<packaging>bundle</packaging>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.hl7.fhir.dstu2016may</artifactId>
|
||||
<packaging>bundle</packaging>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -103,6 +103,7 @@ public class TerminologyCache {
|
|||
private Object lock;
|
||||
private String folder;
|
||||
private Map<String, NamedCache> caches = new HashMap<String, NamedCache>();
|
||||
private static boolean noCaching;
|
||||
|
||||
// use lock from the context
|
||||
public TerminologyCache(Object lock, String folder) throws FileNotFoundException, IOException, FHIRException {
|
||||
|
@ -245,6 +246,9 @@ public class TerminologyCache {
|
|||
}
|
||||
|
||||
public void store(CacheToken cacheToken, boolean persistent, NamedCache nc, CacheEntry e) {
|
||||
if (noCaching) {
|
||||
return;
|
||||
}
|
||||
boolean n = nc.map.containsKey(cacheToken.key);
|
||||
nc.map.put(cacheToken.key, e);
|
||||
if (persistent) {
|
||||
|
@ -426,7 +430,6 @@ public class TerminologyCache {
|
|||
return code.getSystem()+"#"+code.getCode()+": \""+code.getDisplay()+"\"";
|
||||
}
|
||||
|
||||
|
||||
public String summary(CodeableConcept code) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("{");
|
||||
|
@ -440,5 +443,14 @@ public class TerminologyCache {
|
|||
b.append("\"");
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public static boolean isNoCaching() {
|
||||
return noCaching;
|
||||
}
|
||||
|
||||
public static void setNoCaching(boolean noCaching) {
|
||||
TerminologyCache.noCaching = noCaching;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,511 @@
|
|||
package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
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();
|
||||
|
||||
List<String> langs = new ArrayList<>();
|
||||
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, List<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()));
|
||||
}
|
||||
|
||||
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
|
||||
if (cd.hasLanguage() && !langs.contains(cd.getLanguage())) {
|
||||
langs.add(cd.getLanguage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
private void addLanguageRow(ConceptDefinitionComponent c, XhtmlNode t, List<String> langs) {
|
||||
XhtmlNode tr = t.tr();
|
||||
tr.td().addText(c.getCode());
|
||||
for (String lang : langs) {
|
||||
ConceptDefinitionDesignationComponent d = null;
|
||||
for (ConceptDefinitionDesignationComponent designation : c.getDesignation()) {
|
||||
if (designation.hasLanguage()) {
|
||||
if (lang.equals(designation.getLanguage()))
|
||||
d = designation;
|
||||
}
|
||||
}
|
||||
tr.td().addText(d == null ? "" : d.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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,10 @@
|
|||
package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
|
||||
public abstract class SpecialCodeSystem {
|
||||
|
||||
public abstract ConceptDefinitionComponent findConcept(Coding code);
|
||||
|
||||
}
|
|
@ -0,0 +1,495 @@
|
|||
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.ConceptReferenceComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent;
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected String describeLang(String lang) {
|
||||
ValueSet v = context.fetchResource(ValueSet.class, "http://hl7.org/fhir/ValueSet/languages");
|
||||
if (v != null) {
|
||||
ConceptReferenceComponent l = null;
|
||||
for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) {
|
||||
if (cc.getCode().equals(lang))
|
||||
l = cc;
|
||||
}
|
||||
if (l == null) {
|
||||
if (lang.contains("-"))
|
||||
lang = lang.substring(0, lang.indexOf("-"));
|
||||
for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) {
|
||||
if (cc.getCode().equals(lang) || cc.getCode().startsWith(lang+"-"))
|
||||
l = cc;
|
||||
}
|
||||
}
|
||||
if (l != null) {
|
||||
if (lang.contains("-"))
|
||||
lang = lang.substring(0, lang.indexOf("-"));
|
||||
String en = l.getDisplay();
|
||||
String nativelang = null;
|
||||
for (ConceptReferenceDesignationComponent cd : l.getDesignation()) {
|
||||
if (cd.getLanguage().equals(lang))
|
||||
nativelang = cd.getValue();
|
||||
}
|
||||
if (nativelang == null)
|
||||
return en+" ("+lang+")";
|
||||
else
|
||||
return nativelang+" ("+en+", "+lang+")";
|
||||
}
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
||||
public class URICodeSystem extends SpecialCodeSystem {
|
||||
|
||||
@Override
|
||||
public ConceptDefinitionComponent findConcept(Coding code) {
|
||||
if (Utilities.isAbsoluteUrl(code.getCode())) {
|
||||
return new ConceptDefinitionComponent(code.getCode());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -112,10 +112,17 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
|
|||
if (system == null && !code.hasDisplay()) { // dealing with just a plain code (enum)
|
||||
system = systemForCodeInValueSet(code.getCode());
|
||||
}
|
||||
if (!code.hasSystem())
|
||||
if (!code.hasSystem()) {
|
||||
if (options.isGuessSystem() && system == null && Utilities.isAbsoluteUrl(code.getCode())) {
|
||||
system = "urn:ietf:rfc:3986"; // this arises when using URIs bound to value sets
|
||||
}
|
||||
code.setSystem(system);
|
||||
}
|
||||
inExpansion = checkExpansion(code);
|
||||
CodeSystem cs = context.fetchCodeSystem(system);
|
||||
if (cs == null) {
|
||||
cs = findSpecialCodeSystem(system);
|
||||
}
|
||||
if (cs == null) {
|
||||
warningMessage = "Unable to resolve system "+system+" - system is not specified or implicit";
|
||||
if (!inExpansion)
|
||||
|
@ -123,7 +130,7 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
|
|||
}
|
||||
if (cs!=null && cs.getContent() != CodeSystemContentMode.COMPLETE) {
|
||||
warningMessage = "Unable to resolve system "+system+" - system is not complete";
|
||||
if (!inExpansion)
|
||||
if (!inExpansion && cs.getContent() != CodeSystemContentMode.FRAGMENT) // we're going to give it a go if it's a fragment
|
||||
throw new FHIRException(warningMessage);
|
||||
}
|
||||
|
||||
|
@ -147,6 +154,17 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
|
|||
return res;
|
||||
}
|
||||
|
||||
private CodeSystem findSpecialCodeSystem(String system) {
|
||||
if ("urn:ietf:rfc:3986".equals(system)) {
|
||||
CodeSystem cs = new CodeSystem();
|
||||
cs.setUrl(system);
|
||||
cs.setUserData("tx.cs.special", new URICodeSystem());
|
||||
cs.setContent(CodeSystemContentMode.COMPLETE);
|
||||
return cs;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean checkExpansion(Coding code) {
|
||||
if (valueset==null || !valueset.hasExpansion())
|
||||
return false;
|
||||
|
@ -164,9 +182,14 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
|
|||
}
|
||||
|
||||
private ValidationResult validateCode(Coding code, CodeSystem cs) {
|
||||
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), code.getCode());
|
||||
if (cc == null)
|
||||
return new ValidationResult(IssueSeverity.ERROR, context.formatMessage(I18nConstants.UNKNOWN_CODE__IN_, gen(code), cs.getUrl()));
|
||||
ConceptDefinitionComponent cc = cs.hasUserData("tx.cs.special") ? ((SpecialCodeSystem) cs.getUserData("tx.cs.special")).findConcept(code) : findCodeInConcept(cs.getConcept(), code.getCode());
|
||||
if (cc == null) {
|
||||
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
|
||||
return new ValidationResult(IssueSeverity.ERROR, context.formatMessage(I18nConstants.UNKNOWN_CODE__IN_FRAGMENT, gen(code), cs.getUrl()));
|
||||
} else {
|
||||
return new ValidationResult(IssueSeverity.ERROR, context.formatMessage(I18nConstants.UNKNOWN_CODE__IN_, gen(code), cs.getUrl()));
|
||||
}
|
||||
}
|
||||
if (code.getDisplay() == null)
|
||||
return new ValidationResult(cc);
|
||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||
|
|
|
@ -101,6 +101,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
|
|||
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
|
||||
private IWorkerContext context;
|
||||
private boolean canBeHeirarchy = true;
|
||||
private boolean includeAbstract = true;
|
||||
private Set<String> excludeKeys = new HashSet<String>();
|
||||
private Set<String> excludeSystems = new HashSet<String>();
|
||||
private ValueSet focus;
|
||||
|
@ -213,7 +214,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
|
|||
ValueSetExpansionContainsComponent np = null;
|
||||
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
|
||||
boolean inc = CodeSystemUtilities.isInactive(cs, def);
|
||||
if (canBeHeirarchy || !abs)
|
||||
if (includeAbstract || !abs)
|
||||
np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), expParams, abs, inc, filters);
|
||||
for (ConceptDefinitionComponent c : def.getConcept()) {
|
||||
addCodeAndDescendents(cs, system, c, np, expParams, filters, exclusion);
|
||||
|
@ -340,7 +341,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
|
|||
}
|
||||
} else {
|
||||
for (ValueSetExpansionContainsComponent c : codes) {
|
||||
if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
|
||||
if (map.containsKey(key(c)) && (includeAbstract || !c.getAbstract())) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
|
||||
focus.getExpansion().getContains().add(c);
|
||||
c.getContains().clear(); // make sure any heirarchy is wiped
|
||||
}
|
||||
|
@ -379,6 +380,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
|
|||
for (ConceptSetComponent inc : compose.getExclude())
|
||||
excludeCodes(inc, exp.getParameter(), ctxt);
|
||||
canBeHeirarchy = !expParams.getParameterBool("excludeNested") && excludeKeys.isEmpty() && excludeSystems.isEmpty();
|
||||
includeAbstract = !expParams.getParameterBool("excludeNotForUI");
|
||||
boolean first = true;
|
||||
for (ConceptSetComponent inc : compose.getInclude()) {
|
||||
if (first == true)
|
||||
|
@ -560,6 +562,12 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
|
|||
throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
|
||||
for (ConceptDefinitionComponent c : def.getConcept())
|
||||
addCodeAndDescendents(cs, inc.getSystem(), c, null, expParams, imports, null);
|
||||
if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
|
||||
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
|
||||
for (ConceptDefinitionComponent c : children)
|
||||
addCodeAndDescendents(cs, inc.getSystem(), c, null, expParams, imports, null);
|
||||
}
|
||||
|
||||
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
|
||||
// gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's diplsay is 'v'?
|
||||
canBeHeirarchy = false;
|
||||
|
|
|
@ -0,0 +1,996 @@
|
|||
package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.exceptions.DefinitionException;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
|
||||
import org.hl7.fhir.r5.model.BooleanType;
|
||||
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.DataType;
|
||||
import org.hl7.fhir.r5.model.Extension;
|
||||
import org.hl7.fhir.r5.model.ExtensionHelper;
|
||||
import org.hl7.fhir.r5.model.PrimitiveType;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.model.UriType;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
|
||||
import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
|
||||
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
|
||||
import org.hl7.fhir.r5.terminologies.TerminologyRenderer.ConceptMapRenderInstructions;
|
||||
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.xhtml.NodeType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
public class ValueSetRenderer extends TerminologyRenderer {
|
||||
|
||||
private static final String ABSTRACT_CODE_HINT = "This code is not selectable ('Abstract')";
|
||||
|
||||
private String tooCostlyNoteEmpty;
|
||||
private String tooCostlyNoteNotEmpty;
|
||||
private String tooCostlyNoteEmptyDependent;
|
||||
private String tooCostlyNoteNotEmptyDependent;
|
||||
private List<ConceptMapRenderInstructions> renderingMaps = new ArrayList<ConceptMapRenderInstructions>();
|
||||
|
||||
/**
|
||||
*
|
||||
* @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 ValueSetRenderer(TerminologyRendererMode mode, IWorkerContext context, MarkDownProcessor markdown, String prefix, String lang) {
|
||||
super(mode, context, markdown, prefix, lang);
|
||||
renderingMaps.add(new ConceptMapRenderInstructions("Canonical Status", "http://hl7.org/fhir/ValueSet/resource-status", false));
|
||||
}
|
||||
|
||||
public String getTooCostlyNoteEmpty() {
|
||||
return tooCostlyNoteEmpty;
|
||||
}
|
||||
|
||||
public void setTooCostlyNoteEmpty(String tooCostlyNoteEmpty) {
|
||||
this.tooCostlyNoteEmpty = tooCostlyNoteEmpty;
|
||||
}
|
||||
|
||||
public String getTooCostlyNoteNotEmpty() {
|
||||
return tooCostlyNoteNotEmpty;
|
||||
}
|
||||
|
||||
public void setTooCostlyNoteNotEmpty(String tooCostlyNoteNotEmpty) {
|
||||
this.tooCostlyNoteNotEmpty = tooCostlyNoteNotEmpty;
|
||||
}
|
||||
|
||||
public String getTooCostlyNoteEmptyDependent() {
|
||||
return tooCostlyNoteEmptyDependent;
|
||||
}
|
||||
|
||||
public void setTooCostlyNoteEmptyDependent(String tooCostlyNoteEmptyDependent) {
|
||||
this.tooCostlyNoteEmptyDependent = tooCostlyNoteEmptyDependent;
|
||||
}
|
||||
|
||||
public String getTooCostlyNoteNotEmptyDependent() {
|
||||
return tooCostlyNoteNotEmptyDependent;
|
||||
}
|
||||
|
||||
public void setTooCostlyNoteNotEmptyDependent(String tooCostlyNoteNotEmptyDependent) {
|
||||
this.tooCostlyNoteNotEmptyDependent = tooCostlyNoteNotEmptyDependent;
|
||||
}
|
||||
|
||||
public boolean render(ResourceContext rcontext, XhtmlNode x, ValueSet vs, ValueSet src, boolean header) throws FHIRFormatError, DefinitionException, IOException {
|
||||
List<UsedConceptMap> maps = findReleventMaps(vs);
|
||||
|
||||
boolean hasExtensions;
|
||||
if (vs.hasExpansion()) {
|
||||
// for now, we just accept an expansion if there is one
|
||||
hasExtensions = generateExpansion(x, vs, src, header, maps);
|
||||
} else {
|
||||
hasExtensions = generateComposition(rcontext, x, vs, header, maps);
|
||||
}
|
||||
return hasExtensions;
|
||||
}
|
||||
|
||||
private List<UsedConceptMap> findReleventMaps(ValueSet vs) throws FHIRException {
|
||||
List<UsedConceptMap> res = new ArrayList<UsedConceptMap>();
|
||||
for (CanonicalResource md : context.allConformanceResources()) {
|
||||
if (md instanceof ConceptMap) {
|
||||
ConceptMap cm = (ConceptMap) md;
|
||||
if (isSource(vs, cm.getSource())) {
|
||||
ConceptMapRenderInstructions re = findByTarget(cm.getTarget());
|
||||
if (re != null) {
|
||||
ValueSet vst = cm.hasTarget() ? context.fetchResource(ValueSet.class, cm.hasTargetCanonicalType() ? cm.getTargetCanonicalType().getValue() : cm.getTargetUriType().asStringValue()) : null;
|
||||
res.add(new UsedConceptMap(re, vst == null ? cm.getUserString("path") : vst.getUserString("path"), cm));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
// Map<ConceptMap, String> mymaps = new HashMap<ConceptMap, String>();
|
||||
// for (ConceptMap a : context.findMapsForSource(vs.getUrl())) {
|
||||
// String url = "";
|
||||
// ValueSet vsr = context.fetchResource(ValueSet.class, ((Reference) a.getTarget()).getReference());
|
||||
// if (vsr != null)
|
||||
// url = (String) vsr.getUserData("filename");
|
||||
// mymaps.put(a, url);
|
||||
// }
|
||||
// Map<ConceptMap, String> mymaps = new HashMap<ConceptMap, String>();
|
||||
// for (ConceptMap a : context.findMapsForSource(cs.getValueSet())) {
|
||||
// String url = "";
|
||||
// ValueSet vsr = context.fetchResource(ValueSet.class, ((Reference) a.getTarget()).getReference());
|
||||
// if (vsr != null)
|
||||
// url = (String) vsr.getUserData("filename");
|
||||
// mymaps.put(a, url);
|
||||
// }
|
||||
// also, look in the contained resources for a concept map
|
||||
// for (Resource r : cs.getContained()) {
|
||||
// if (r instanceof ConceptMap) {
|
||||
// ConceptMap cm = (ConceptMap) r;
|
||||
// if (((Reference) cm.getSource()).getReference().equals(cs.getValueSet())) {
|
||||
// String url = "";
|
||||
// ValueSet vsr = context.fetchResource(ValueSet.class, ((Reference) cm.getTarget()).getReference());
|
||||
// if (vsr != null)
|
||||
// url = (String) vsr.getUserData("filename");
|
||||
// mymaps.put(cm, url);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private boolean isSource(ValueSet vs, DataType source) {
|
||||
return vs.hasUrl() && source != null && vs.getUrl().equals(source.primitiveValue());
|
||||
}
|
||||
|
||||
private boolean generateExpansion(XhtmlNode x, ValueSet vs, ValueSet src, boolean header, List<UsedConceptMap> maps) throws FHIRFormatError, DefinitionException, IOException {
|
||||
boolean hasExtensions = false;
|
||||
List<String> langs = new ArrayList<String>();
|
||||
|
||||
|
||||
if (header) {
|
||||
XhtmlNode h = x.addTag(getHeader());
|
||||
h.tx("Value Set Contents");
|
||||
if (IsNotFixedExpansion(vs))
|
||||
addMarkdown(x, vs.getDescription());
|
||||
if (vs.hasCopyright())
|
||||
generateCopyright(x, vs);
|
||||
}
|
||||
if (ToolingExtensions.hasExtension(vs.getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY)) {
|
||||
List<Extension> exl = vs.getExpansion().getExtensionsByUrl(ToolingExtensions.EXT_EXP_TOOCOSTLY);
|
||||
boolean other = false;
|
||||
for (Extension ex : exl) {
|
||||
if (ex.getValue() instanceof BooleanType) {
|
||||
x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(vs.getExpansion().getContains().isEmpty() ? tooCostlyNoteEmpty : tooCostlyNoteNotEmpty );
|
||||
} else if (!other) {
|
||||
x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(vs.getExpansion().getContains().isEmpty() ? tooCostlyNoteEmptyDependent : tooCostlyNoteNotEmptyDependent );
|
||||
other = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Integer count = countMembership(vs);
|
||||
if (count == null)
|
||||
x.para().tx("This value set does not contain a fixed number of concepts");
|
||||
else
|
||||
x.para().tx("This value set contains "+count.toString()+" concepts");
|
||||
}
|
||||
if (ToolingExtensions.hasExtension(vs.getExpansion(), ToolingExtensions.EXT_EXP_FRAGMENT)) {
|
||||
XhtmlNode div = x.div().style("border: maroon 1px solid; background-color: #FFCCCC; padding: 8px");
|
||||
List<Extension> exl = vs.getExpansion().getExtensionsByUrl(ToolingExtensions.EXT_EXP_FRAGMENT);
|
||||
if (exl.size() > 1) {
|
||||
div.para().addText("Warning: this expansion is generated from fragments of the following code systems, and may be missing codes, or include codes that are not valid:");
|
||||
XhtmlNode ul = div.ul();
|
||||
for (Extension ex : exl) {
|
||||
addCSRef(ul.li(), ex.getValue().primitiveValue());
|
||||
}
|
||||
} else {
|
||||
XhtmlNode p = div.para();
|
||||
p.addText("Warning: this expansion is generated from a fragment of the code system ");
|
||||
addCSRef(p, exl.get(0).getValue().primitiveValue());
|
||||
p.addText(" and may be missing codes, or include codes that are not valid");
|
||||
}
|
||||
}
|
||||
|
||||
generateVersionNotice(x, vs.getExpansion());
|
||||
|
||||
CodeSystem allCS = null;
|
||||
boolean doLevel = false;
|
||||
for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) {
|
||||
if (cc.hasContains()) {
|
||||
doLevel = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boolean doSystem = true; // checkDoSystem(vs, src);
|
||||
boolean doDefinition = checkDoDefinition(vs.getExpansion().getContains());
|
||||
if (doSystem && allFromOneSystem(vs)) {
|
||||
doSystem = false;
|
||||
XhtmlNode p = x.para();
|
||||
p.tx("All codes from system ");
|
||||
allCS = context.fetchCodeSystem(vs.getExpansion().getContains().get(0).getSystem());
|
||||
String ref = null;
|
||||
if (allCS != null)
|
||||
ref = getCsRef(allCS);
|
||||
if (ref == null)
|
||||
p.code(vs.getExpansion().getContains().get(0).getSystem());
|
||||
else
|
||||
p.ah(prefix+ref).code(vs.getExpansion().getContains().get(0).getSystem());
|
||||
}
|
||||
XhtmlNode t = x.table( "codes");
|
||||
XhtmlNode tr = t.tr();
|
||||
if (doLevel)
|
||||
tr.td().b().tx("Lvl");
|
||||
tr.td().attribute("style", "white-space:nowrap").b().tx("Code");
|
||||
if (doSystem)
|
||||
tr.td().b().tx("System");
|
||||
tr.td().b().tx("Display");
|
||||
if (doDefinition)
|
||||
tr.td().b().tx("Definition");
|
||||
|
||||
addMapHeaders(tr, maps);
|
||||
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
|
||||
addExpansionRowToTable(t, c, 0, doLevel, doSystem, doDefinition, maps, allCS, langs);
|
||||
}
|
||||
|
||||
// now, build observed languages
|
||||
|
||||
if (langs.size() > 0) {
|
||||
Collections.sort(langs);
|
||||
x.para().b().tx("Additional Language Displays");
|
||||
t = x.table( "codes");
|
||||
tr = t.tr();
|
||||
tr.td().b().tx("Code");
|
||||
for (String lang : langs)
|
||||
tr.td().b().addText(describeLang(lang));
|
||||
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
|
||||
addLanguageRow(c, t, langs);
|
||||
}
|
||||
}
|
||||
|
||||
return hasExtensions;
|
||||
}
|
||||
|
||||
private boolean checkDoSystem(ValueSet vs, ValueSet src) {
|
||||
if (src != null)
|
||||
vs = src;
|
||||
return vs.hasCompose();
|
||||
}
|
||||
|
||||
private boolean IsNotFixedExpansion(ValueSet vs) {
|
||||
if (vs.hasCompose())
|
||||
return false;
|
||||
|
||||
|
||||
// it's not fixed if it has any includes that are not version fixed
|
||||
for (ConceptSetComponent cc : vs.getCompose().getInclude()) {
|
||||
if (cc.hasValueSet())
|
||||
return true;
|
||||
if (!cc.hasVersion())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private ConceptMapRenderInstructions findByTarget(DataType source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
String src = source.primitiveValue();
|
||||
if (src != null)
|
||||
for (ConceptMapRenderInstructions t : renderingMaps) {
|
||||
if (src.equals(t.getUrl()))
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private Integer countMembership(ValueSet vs) {
|
||||
int count = 0;
|
||||
if (vs.hasExpansion())
|
||||
count = count + conceptCount(vs.getExpansion().getContains());
|
||||
else {
|
||||
if (vs.hasCompose()) {
|
||||
if (vs.getCompose().hasExclude()) {
|
||||
try {
|
||||
ValueSetExpansionOutcome vse = context.expandVS(vs, true, false);
|
||||
count = 0;
|
||||
count += conceptCount(vse.getValueset().getExpansion().getContains());
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
|
||||
if (inc.hasFilter())
|
||||
return null;
|
||||
if (!inc.hasConcept())
|
||||
return null;
|
||||
count = count + inc.getConcept().size();
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int conceptCount(List<ValueSetExpansionContainsComponent> list) {
|
||||
int count = 0;
|
||||
for (ValueSetExpansionContainsComponent c : list) {
|
||||
if (!c.getAbstract())
|
||||
count++;
|
||||
count = count + conceptCount(c.getContains());
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void addCSRef(XhtmlNode x, String url) {
|
||||
CodeSystem cs = context.fetchCodeSystem(url);
|
||||
if (cs == null) {
|
||||
x.code(url);
|
||||
} else if (cs.hasUserData("path")) {
|
||||
x.ah(cs.getUserString("path")).tx(cs.present());
|
||||
} else {
|
||||
x.code(url);
|
||||
x.tx(" ("+cs.present()+")");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private void generateVersionNotice(XhtmlNode x, ValueSetExpansionComponent expansion) {
|
||||
Multimap<String, String> versions = HashMultimap.create();
|
||||
for (ValueSetExpansionParameterComponent p : expansion.getParameter()) {
|
||||
if (p.getName().equals("version")) {
|
||||
String[] parts = ((PrimitiveType) p.getValue()).asStringValue().split("\\|");
|
||||
if (parts.length == 2)
|
||||
versions.put(parts[0], parts[1]);
|
||||
}
|
||||
}
|
||||
if (versions.size() > 0) {
|
||||
XhtmlNode div = null;
|
||||
XhtmlNode ul = null;
|
||||
boolean first = true;
|
||||
for (String s : versions.keySet()) {
|
||||
if (versions.size() == 1 && versions.get(s).size() == 1) {
|
||||
for (String v : versions.get(s)) { // though there'll only be one
|
||||
XhtmlNode p = x.para().style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px");
|
||||
p.tx("Expansion based on ");
|
||||
expRef(p, s, v);
|
||||
}
|
||||
} else {
|
||||
for (String v : versions.get(s)) {
|
||||
if (first) {
|
||||
div = x.div().style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px");
|
||||
div.para().tx("Expansion based on: ");
|
||||
ul = div.ul();
|
||||
first = false;
|
||||
}
|
||||
expRef(ul.li(), s, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void expRef(XhtmlNode x, String u, String v) {
|
||||
// TODO Auto-generated method stub
|
||||
if (u.equals("http://snomed.info/sct")) {
|
||||
String[] parts = v.split("\\/");
|
||||
if (parts.length >= 5) {
|
||||
String m = describeModule(parts[4]);
|
||||
if (parts.length == 7) {
|
||||
x.tx("SNOMED CT "+m+" edition "+formatSCTDate(parts[6]));
|
||||
} else {
|
||||
x.tx("SNOMED CT "+m+" edition");
|
||||
}
|
||||
} else {
|
||||
x.tx(describeSystem(u)+" version "+v);
|
||||
}
|
||||
} else if (u.equals("http://loinc.org")) {
|
||||
String vd = describeLoincVer(v);
|
||||
if (vd != null) {
|
||||
x.tx("Loinc v"+v+" ("+vd+")");
|
||||
} else {
|
||||
x.tx("Loinc v"+v);
|
||||
}
|
||||
} else {
|
||||
CanonicalResource cr = (CanonicalResource) context.fetchResource(Resource.class, u+"|"+v);
|
||||
if (cr != null) {
|
||||
if (cr.hasUserData("path")) {
|
||||
x.ah(cr.getUserString("path")).tx(cr.present()+" v"+v+" ("+cr.fhirType()+")");
|
||||
} else {
|
||||
x.tx(describeSystem(u)+" v"+v+" ("+cr.fhirType()+")");
|
||||
}
|
||||
} else {
|
||||
x.tx(describeSystem(u)+" version "+v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String describeLoincVer(String v) {
|
||||
if ("2.67".equals(v)) return "Dec 2019";
|
||||
if ("2.66".equals(v)) return "Jun 2019";
|
||||
if ("2.65".equals(v)) return "Dec 2018";
|
||||
if ("2.64".equals(v)) return "Jun 2018";
|
||||
if ("2.63".equals(v)) return "Dec 2017";
|
||||
if ("2.61".equals(v)) return "Jun 2017";
|
||||
if ("2.59".equals(v)) return "Feb 2017";
|
||||
if ("2.58".equals(v)) return "Dec 2016";
|
||||
if ("2.56".equals(v)) return "Jun 2016";
|
||||
if ("2.54".equals(v)) return "Dec 2015";
|
||||
if ("2.52".equals(v)) return "Jun 2015";
|
||||
if ("2.50".equals(v)) return "Dec 2014";
|
||||
if ("2.48".equals(v)) return "Jun 2014";
|
||||
if ("2.46".equals(v)) return "Dec 2013";
|
||||
if ("2.44".equals(v)) return "Jun 2013";
|
||||
if ("2.42".equals(v)) return "Dec 2012";
|
||||
if ("2.40".equals(v)) return "Jun 2012";
|
||||
if ("2.38".equals(v)) return "Dec 2011";
|
||||
if ("2.36".equals(v)) return "Jun 2011";
|
||||
if ("2.34".equals(v)) return "Dec 2010";
|
||||
if ("2.32".equals(v)) return "Jun 2010";
|
||||
if ("2.30".equals(v)) return "Feb 2010";
|
||||
if ("2.29".equals(v)) return "Dec 2009";
|
||||
if ("2.27".equals(v)) return "Jul 2009";
|
||||
if ("2.26".equals(v)) return "Jan 2009";
|
||||
if ("2.24".equals(v)) return "Jul 2008";
|
||||
if ("2.22".equals(v)) return "Dec 2007";
|
||||
if ("2.21".equals(v)) return "Jun 2007";
|
||||
if ("2.19".equals(v)) return "Dec 2006";
|
||||
if ("2.17".equals(v)) return "Jun 2006";
|
||||
if ("2.16".equals(v)) return "Dec 2005";
|
||||
if ("2.15".equals(v)) return "Jun 2005";
|
||||
if ("2.14".equals(v)) return "Dec 2004";
|
||||
if ("2.13".equals(v)) return "Aug 2004";
|
||||
if ("2.12".equals(v)) return "Feb 2004";
|
||||
if ("2.10".equals(v)) return "Oct 2003";
|
||||
if ("2.09".equals(v)) return "May 2003";
|
||||
if ("2.08 ".equals(v)) return "Sep 2002";
|
||||
if ("2.07".equals(v)) return "Aug 2002";
|
||||
if ("2.05".equals(v)) return "Feb 2002";
|
||||
if ("2.04".equals(v)) return "Jan 2002";
|
||||
if ("2.03".equals(v)) return "Jul 2001";
|
||||
if ("2.02".equals(v)) return "May 2001";
|
||||
if ("2.01".equals(v)) return "Jan 2001";
|
||||
if ("2.00".equals(v)) return "Jan 2001";
|
||||
if ("1.0n".equals(v)) return "Feb 2000";
|
||||
if ("1.0ma".equals(v)) return "Aug 1999";
|
||||
if ("1.0m".equals(v)) return "Jul 1999";
|
||||
if ("1.0l".equals(v)) return "Jan 1998";
|
||||
if ("1.0ja".equals(v)) return "Oct 1997";
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String formatSCTDate(String ds) {
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
|
||||
Date date;
|
||||
try {
|
||||
date = format.parse(ds);
|
||||
} catch (ParseException e) {
|
||||
return ds;
|
||||
}
|
||||
return new SimpleDateFormat("dd-MMM yyyy").format(date);
|
||||
}
|
||||
|
||||
private String describeModule(String module) {
|
||||
if ("900000000000207008".equals(module))
|
||||
return "International";
|
||||
if ("731000124108".equals(module))
|
||||
return "United States";
|
||||
if ("32506021000036107".equals(module))
|
||||
return "Australian";
|
||||
if ("449081005".equals(module))
|
||||
return "Spanish";
|
||||
if ("554471000005108".equals(module))
|
||||
return "Danish";
|
||||
if ("11000146104".equals(module))
|
||||
return "Dutch";
|
||||
if ("45991000052106".equals(module))
|
||||
return "Swedish";
|
||||
if ("999000041000000102".equals(module))
|
||||
return "United Kingdon";
|
||||
return module;
|
||||
}
|
||||
|
||||
private boolean hasVersionParameter(ValueSetExpansionComponent expansion) {
|
||||
for (ValueSetExpansionParameterComponent p : expansion.getParameter()) {
|
||||
if (p.getName().equals("version"))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addLanguageRow(ValueSetExpansionContainsComponent c, XhtmlNode t, List<String> langs) {
|
||||
XhtmlNode tr = t.tr();
|
||||
tr.td().addText(c.getCode());
|
||||
for (String lang : langs) {
|
||||
String d = null;
|
||||
for (Extension ext : c.getExtension()) {
|
||||
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
|
||||
String l = ToolingExtensions.readStringExtension(ext, "lang");
|
||||
if (lang.equals(l))
|
||||
d = ToolingExtensions.readStringExtension(ext, "content");
|
||||
}
|
||||
}
|
||||
tr.td().addText(d == null ? "" : d);
|
||||
}
|
||||
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
|
||||
addLanguageRow(cc, t, langs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean checkDoDefinition(List<ValueSetExpansionContainsComponent> contains) {
|
||||
for (ValueSetExpansionContainsComponent c : contains) {
|
||||
CodeSystem cs = context.fetchCodeSystem(c.getSystem());
|
||||
if (cs != null)
|
||||
return true;
|
||||
if (checkDoDefinition(c.getContains()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private boolean allFromOneSystem(ValueSet vs) {
|
||||
if (vs.getExpansion().getContains().isEmpty())
|
||||
return false;
|
||||
String system = vs.getExpansion().getContains().get(0).getSystem();
|
||||
for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) {
|
||||
if (!checkSystemMatches(system, cc))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getCsRef(String system) {
|
||||
CodeSystem cs = context.fetchCodeSystem(system);
|
||||
return getCsRef(cs);
|
||||
}
|
||||
|
||||
private <T extends Resource> String getCsRef(T cs) {
|
||||
String ref = (String) cs.getUserData("filename");
|
||||
if (ref == null)
|
||||
ref = (String) cs.getUserData("path");
|
||||
if (ref == null)
|
||||
return "?ngen-14?.html";
|
||||
if (!ref.contains(".html"))
|
||||
ref = ref + ".html";
|
||||
return ref.replace("\\", "/");
|
||||
}
|
||||
|
||||
private void addExpansionRowToTable(XhtmlNode t, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doSystem, boolean doDefinition, List<UsedConceptMap> maps, CodeSystem allCS, List<String> langs) {
|
||||
XhtmlNode tr = t.tr();
|
||||
XhtmlNode td = tr.td();
|
||||
|
||||
String tgt = makeAnchor(c.getSystem(), c.getCode());
|
||||
td.an(tgt);
|
||||
|
||||
if (doLevel) {
|
||||
td.addText(Integer.toString(i));
|
||||
td = tr.td();
|
||||
}
|
||||
String s = Utilities.padLeft("", '\u00A0', i*2);
|
||||
td.attribute("style", "white-space:nowrap").addText(s);
|
||||
addCodeToTable(c.getAbstract(), c.getSystem(), c.getCode(), c.getDisplay(), td);
|
||||
if (doSystem) {
|
||||
td = tr.td();
|
||||
td.addText(c.getSystem());
|
||||
}
|
||||
td = tr.td();
|
||||
if (c.hasDisplayElement())
|
||||
td.addText(c.getDisplay());
|
||||
|
||||
if (doDefinition) {
|
||||
CodeSystem cs = allCS;
|
||||
if (cs == null)
|
||||
cs = context.fetchCodeSystem(c.getSystem());
|
||||
td = tr.td();
|
||||
if (cs != null)
|
||||
td.addText(CodeSystemUtilities.getCodeDefinition(cs, c.getCode()));
|
||||
}
|
||||
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.getRelationship().toString());
|
||||
span.addText(getCharForRelationship(mapping.comp));
|
||||
addRefToCode(td, mapping.group.getTarget(), m.getLink(), mapping.comp.getCode());
|
||||
if (!Utilities.noString(mapping.comp.getComment()))
|
||||
td.i().tx("("+mapping.comp.getComment()+")");
|
||||
}
|
||||
}
|
||||
for (Extension ext : c.getExtension()) {
|
||||
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
|
||||
String lang = ToolingExtensions.readStringExtension(ext, "lang");
|
||||
if (!Utilities.noString(lang) && !langs.contains(lang))
|
||||
langs.add(lang);
|
||||
}
|
||||
}
|
||||
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
|
||||
addExpansionRowToTable(t, cc, i+1, doLevel, doSystem, doDefinition, maps, allCS, langs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean checkSystemMatches(String system, ValueSetExpansionContainsComponent cc) {
|
||||
if (!system.equals(cc.getSystem()))
|
||||
return false;
|
||||
for (ValueSetExpansionContainsComponent cc1 : cc.getContains()) {
|
||||
if (!checkSystemMatches(system, cc1))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void addCodeToTable(boolean isAbstract, String system, String code, String display, XhtmlNode td) {
|
||||
CodeSystem e = context.fetchCodeSystem(system);
|
||||
if (e == null || e.getContent() != org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode.COMPLETE) {
|
||||
if (isAbstract)
|
||||
td.i().setAttribute("title", ABSTRACT_CODE_HINT).addText(code);
|
||||
else if ("http://snomed.info/sct".equals(system)) {
|
||||
td.ah(sctLink(code)).addText(code);
|
||||
} else if ("http://loinc.org".equals(system)) {
|
||||
td.ah("http://details.loinc.org/LOINC/"+code+".html").addText(code);
|
||||
} else
|
||||
td.addText(code);
|
||||
} else {
|
||||
String href = prefix+getCsRef(e);
|
||||
if (href.contains("#"))
|
||||
href = href + "-"+Utilities.nmtokenize(code);
|
||||
else
|
||||
href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(code);
|
||||
if (isAbstract)
|
||||
td.ah(href).setAttribute("title", ABSTRACT_CODE_HINT).i().addText(code);
|
||||
else
|
||||
td.ah(href).addText(code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String sctLink(String code) {
|
||||
// if (snomedEdition != null)
|
||||
// http://browser.ihtsdotools.org/?perspective=full&conceptId1=428041000124106&edition=us-edition&release=v20180301&server=https://prod-browser-exten.ihtsdotools.org/api/snomed&langRefset=900000000000509007
|
||||
return "http://browser.ihtsdotools.org/?perspective=full&conceptId1="+code;
|
||||
}
|
||||
|
||||
private void addRefToCode(XhtmlNode td, String target, String vslink, String code) {
|
||||
CodeSystem cs = context.fetchCodeSystem(target);
|
||||
String cslink = getCsRef(cs);
|
||||
XhtmlNode a = null;
|
||||
if (cslink != null)
|
||||
a = td.ah(prefix+cslink+"#"+cs.getId()+"-"+code);
|
||||
else
|
||||
a = td.ah(prefix+vslink+"#"+code);
|
||||
a.addText(code);
|
||||
}
|
||||
|
||||
private boolean generateComposition(ResourceContext rcontext, XhtmlNode x, ValueSet vs, boolean header, List<UsedConceptMap> maps) throws FHIRException, IOException {
|
||||
boolean hasExtensions = false;
|
||||
List<String> langs = new ArrayList<String>();
|
||||
|
||||
if (header) {
|
||||
XhtmlNode h = x.h2();
|
||||
h.addText(vs.present());
|
||||
addMarkdown(x, vs.getDescription());
|
||||
if (vs.hasCopyrightElement())
|
||||
generateCopyright(x, vs);
|
||||
}
|
||||
XhtmlNode ul = x.ul();
|
||||
if (vs.getCompose().getInclude().size() == 1 && vs.getCompose().getExclude().size() == 0) {
|
||||
hasExtensions = genInclude(rcontext, ul, vs.getCompose().getInclude().get(0), "Include", langs, maps) || hasExtensions;
|
||||
} else {
|
||||
XhtmlNode p = x.para();
|
||||
p.tx("This value set includes codes based on the following rules:");
|
||||
|
||||
XhtmlNode li;
|
||||
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
|
||||
hasExtensions = genInclude(rcontext, ul, inc, "Include", langs, maps) || hasExtensions;
|
||||
}
|
||||
for (ConceptSetComponent exc : vs.getCompose().getExclude()) {
|
||||
hasExtensions = genInclude(rcontext, ul, exc, "Exclude", langs, maps) || hasExtensions;
|
||||
}
|
||||
}
|
||||
|
||||
// now, build observed languages
|
||||
|
||||
if (langs.size() > 0) {
|
||||
Collections.sort(langs);
|
||||
x.para().b().tx("Additional Language Displays");
|
||||
XhtmlNode t = x.table( "codes");
|
||||
XhtmlNode tr = t.tr();
|
||||
tr.td().b().tx("Code");
|
||||
for (String lang : langs)
|
||||
tr.td().b().addText(describeLang(lang));
|
||||
for (ConceptSetComponent c : vs.getCompose().getInclude()) {
|
||||
for (ConceptReferenceComponent cc : c.getConcept()) {
|
||||
addLanguageRow(cc, t, langs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasExtensions;
|
||||
}
|
||||
|
||||
private boolean genInclude(ResourceContext rcontext, XhtmlNode ul, ConceptSetComponent inc, String type, List<String> langs, List<UsedConceptMap> maps) throws FHIRException, IOException {
|
||||
boolean hasExtensions = false;
|
||||
XhtmlNode li;
|
||||
li = ul.li();
|
||||
CodeSystem e = context.fetchCodeSystem(inc.getSystem());
|
||||
|
||||
if (inc.hasSystem()) {
|
||||
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
|
||||
li.addText(type+" all codes defined in ");
|
||||
addCsRef(inc, li, e);
|
||||
} else {
|
||||
if (inc.getConcept().size() > 0) {
|
||||
li.addText(type+" these codes as defined in ");
|
||||
addCsRef(inc, li, e);
|
||||
if (inc.hasVersion()) {
|
||||
li.addText(" version ");
|
||||
li.code(inc.getVersion());
|
||||
}
|
||||
|
||||
XhtmlNode t = li.table("none");
|
||||
boolean hasComments = false;
|
||||
boolean hasDefinition = false;
|
||||
for (ConceptReferenceComponent c : inc.getConcept()) {
|
||||
hasComments = hasComments || ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT);
|
||||
hasDefinition = hasDefinition || ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION);
|
||||
}
|
||||
if (hasComments || hasDefinition)
|
||||
hasExtensions = true;
|
||||
addMapHeaders(addTableHeaderRowStandard(t, false, true, hasDefinition, hasComments, false, false, null), maps);
|
||||
for (ConceptReferenceComponent c : inc.getConcept()) {
|
||||
XhtmlNode tr = t.tr();
|
||||
XhtmlNode td = tr.td();
|
||||
ConceptDefinitionComponent cc = getConceptForCode(e, c.getCode(), inc);
|
||||
addCodeToTable(false, inc.getSystem(), c.getCode(), c.hasDisplay()? c.getDisplay() : cc != null ? cc.getDisplay() : "", td);
|
||||
|
||||
td = tr.td();
|
||||
if (!Utilities.noString(c.getDisplay()))
|
||||
td.addText(c.getDisplay());
|
||||
else if (cc != null && !Utilities.noString(cc.getDisplay()))
|
||||
td.addText(cc.getDisplay());
|
||||
|
||||
td = tr.td();
|
||||
if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION))
|
||||
smartAddText(td, ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_DEFINITION));
|
||||
else if (cc != null && !Utilities.noString(cc.getDefinition()))
|
||||
smartAddText(td, cc.getDefinition());
|
||||
|
||||
if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT)) {
|
||||
smartAddText(tr.td(), "Note: "+ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_VS_COMMENT));
|
||||
}
|
||||
for (ConceptReferenceDesignationComponent cd : c.getDesignation()) {
|
||||
if (cd.hasLanguage() && !langs.contains(cd.getLanguage()))
|
||||
langs.add(cd.getLanguage());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inc.getFilter().size() > 0) {
|
||||
li.addText(type+" codes from ");
|
||||
addCsRef(inc, li, e);
|
||||
li.tx(" where ");
|
||||
for (int i = 0; i < inc.getFilter().size(); i++) {
|
||||
ConceptSetFilterComponent f = inc.getFilter().get(i);
|
||||
if (i > 0) {
|
||||
if (i == inc.getFilter().size()-1) {
|
||||
li.tx(" and ");
|
||||
} else {
|
||||
li.tx(", ");
|
||||
}
|
||||
}
|
||||
if (f.getOp() == FilterOperator.EXISTS) {
|
||||
if (f.getValue().equals("true")) {
|
||||
li.tx(f.getProperty()+" exists");
|
||||
} else {
|
||||
li.tx(f.getProperty()+" doesn't exist");
|
||||
}
|
||||
} else {
|
||||
li.tx(f.getProperty()+" "+describe(f.getOp())+" ");
|
||||
if (e != null && codeExistsInValueSet(e, f.getValue())) {
|
||||
String href = prefix+getCsRef(e);
|
||||
if (href.contains("#"))
|
||||
href = href + "-"+Utilities.nmtokenize(f.getValue());
|
||||
else
|
||||
href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(f.getValue());
|
||||
li.ah(href).addText(f.getValue());
|
||||
} else if ("concept".equals(f.getProperty()) && inc.hasSystem()) {
|
||||
li.addText(f.getValue());
|
||||
ValidationResult vr = context.validateCode(terminologyServiceOptions, inc.getSystem(), f.getValue(), null);
|
||||
if (vr.isOk()) {
|
||||
li.tx(" ("+vr.getDisplay()+")");
|
||||
}
|
||||
}
|
||||
else
|
||||
li.addText(f.getValue());
|
||||
String disp = ToolingExtensions.getDisplayHint(f);
|
||||
if (disp != null)
|
||||
li.tx(" ("+disp+")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inc.hasValueSet()) {
|
||||
li.tx(", where the codes are contained in ");
|
||||
boolean first = true;
|
||||
for (UriType vs : inc.getValueSet()) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
li.tx(", ");
|
||||
AddVsRef(rcontext, vs.asStringValue(), li);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
li.tx("Import all the codes that are contained in ");
|
||||
boolean first = true;
|
||||
for (UriType vs : inc.getValueSet()) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
li.tx(", ");
|
||||
AddVsRef(rcontext, vs.asStringValue(), li);
|
||||
}
|
||||
}
|
||||
return hasExtensions;
|
||||
}
|
||||
|
||||
|
||||
private ConceptDefinitionComponent getConceptForCode(CodeSystem e, String code, ConceptSetComponent inc) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
// first, look in the code systems
|
||||
if (e == null)
|
||||
e = context.fetchCodeSystem(inc.getSystem());
|
||||
if (e != null) {
|
||||
ConceptDefinitionComponent v = getConceptForCode(e.getConcept(), code);
|
||||
if (v != null)
|
||||
return v;
|
||||
}
|
||||
|
||||
if (noSlowLookup)
|
||||
return null;
|
||||
|
||||
if (!context.hasCache()) {
|
||||
ValueSetExpansionComponent vse;
|
||||
try {
|
||||
ValueSetExpansionOutcome vso = context.expandVS(inc, false);
|
||||
ValueSet valueset = vso.getValueset();
|
||||
if (valueset == null)
|
||||
throw new TerminologyServiceException("Error Expanding ValueSet: "+vso.getError());
|
||||
vse = valueset.getExpansion();
|
||||
|
||||
} catch (TerminologyServiceException e1) {
|
||||
return null;
|
||||
}
|
||||
if (vse != null) {
|
||||
ConceptDefinitionComponent v = getConceptForCodeFromExpansion(vse.getContains(), code);
|
||||
if (v != null)
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
return context.validateCode(terminologyServiceOptions, inc.getSystem(), code, null).asConceptDefinition();
|
||||
}
|
||||
|
||||
private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> list, String code) {
|
||||
for (ConceptDefinitionComponent c : list) {
|
||||
if (code.equals(c.getCode()))
|
||||
return c;
|
||||
ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code);
|
||||
if (v != null)
|
||||
return v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ConceptDefinitionComponent getConceptForCodeFromExpansion(List<ValueSetExpansionContainsComponent> list, String code) {
|
||||
for (ValueSetExpansionContainsComponent c : list) {
|
||||
if (code.equals(c.getCode())) {
|
||||
ConceptDefinitionComponent res = new ConceptDefinitionComponent();
|
||||
res.setCode(c.getCode());
|
||||
res.setDisplay(c.getDisplay());
|
||||
return res;
|
||||
}
|
||||
ConceptDefinitionComponent v = getConceptForCodeFromExpansion(c.getContains(), code);
|
||||
if (v != null)
|
||||
return v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private boolean codeExistsInValueSet(CodeSystem cs, String code) {
|
||||
for (ConceptDefinitionComponent c : cs.getConcept()) {
|
||||
if (inConcept(code, c))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void addLanguageRow(ConceptReferenceComponent c, XhtmlNode t, List<String> langs) {
|
||||
XhtmlNode tr = t.tr();
|
||||
tr.td().addText(c.getCode());
|
||||
for (String lang : langs) {
|
||||
String d = null;
|
||||
for (ConceptReferenceDesignationComponent cd : c.getDesignation()) {
|
||||
String l = cd.getLanguage();
|
||||
if (lang.equals(l))
|
||||
d = cd.getValue();
|
||||
}
|
||||
tr.td().addText(d == null ? "" : d);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String describe(FilterOperator op) {
|
||||
if (op == null)
|
||||
return " null ";
|
||||
switch (op) {
|
||||
case EQUAL: return " = ";
|
||||
case ISA: return " is-a ";
|
||||
case ISNOTA: return " is-not-a ";
|
||||
case REGEX: return " matches (by regex) ";
|
||||
case NULL: return " ?ngen-13? ";
|
||||
case IN: return " in ";
|
||||
case NOTIN: return " not in ";
|
||||
case DESCENDENTOF: return " descends from ";
|
||||
case EXISTS: return " exists ";
|
||||
case GENERALIZES: return " generalizes ";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean inConcept(String code, ConceptDefinitionComponent c) {
|
||||
if (c.hasCodeElement() && c.getCode().equals(code))
|
||||
return true;
|
||||
for (ConceptDefinitionComponent g : c.getConcept()) {
|
||||
if (inConcept(code, g))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
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.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()+")" : "");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -419,6 +419,7 @@ public class I18nConstants {
|
|||
public final static String UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER_USE_PARAMETER_TX_NA_TUN_RUN_WITHOUT_USING_TERMINOLOGY_SERVICES_TO_VALIDATE_LOINC_SNOMED_ICDX_ETC_ERROR__ = "Unable_to_connect_to_terminology_server_Use_parameter_tx_na_tun_run_without_using_terminology_services_to_validate_LOINC_SNOMED_ICDX_etc_Error__";
|
||||
public final static String DISPLAY_NAME_FOR__SHOULD_BE_ONE_OF__INSTEAD_OF_ = "Display_Name_for__should_be_one_of__instead_of_";
|
||||
public final static String UNKNOWN_CODE__IN_ = "Unknown_Code__in_";
|
||||
public final static String UNKNOWN_CODE__IN_FRAGMENT = "UNKNOWN_CODE__IN_FRAGMENT";
|
||||
public final static String CODE_FOUND_IN_EXPANSION_HOWEVER_ = "Code_found_in_expansion_however_";
|
||||
public final static String NONE_OF_THE_PROVIDED_CODES_ARE_IN_THE_VALUE_SET_ = "None_of_the_provided_codes_are_in_the_value_set_";
|
||||
public final static String CODING_HAS_NO_SYSTEM__CANNOT_VALIDATE = "Coding_has_no_system__cannot_validate";
|
||||
|
@ -504,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";
|
||||
}
|
|
@ -419,6 +419,7 @@ Error_parsing_ = Error parsing {0}:{1}
|
|||
Unable_to_connect_to_terminology_server_Use_parameter_tx_na_tun_run_without_using_terminology_services_to_validate_LOINC_SNOMED_ICDX_etc_Error__ = Unable to connect to terminology server. Use parameter ''-tx n/a'' tun run without using terminology services to validate LOINC, SNOMED, ICD-X etc. Error = {0}
|
||||
Display_Name_for__should_be_one_of__instead_of_ = Display Name for {0}#{1} should be one of ''{2}'' instead of ''{3}''
|
||||
Unknown_Code__in_ = Unknown Code {0} in {1}
|
||||
UNKNOWN_CODE__IN_FRAGMENT = Unknown Code {0} in {1} - note that the code system is labelled as a fragment, so the code may be valid in some other fragment
|
||||
Code_found_in_expansion_however_ = Code found in expansion, however: {0}
|
||||
None_of_the_provided_codes_are_in_the_value_set_ = None of the provided codes are in the value set {0}
|
||||
Coding_has_no_system__cannot_validate = Coding has no system - cannot validate
|
||||
|
@ -481,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
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.core</artifactId>
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -243,6 +243,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
|
|||
private Set<String> loadedIgs = new HashSet<>();
|
||||
private IValidatorResourceFetcher fetcher;
|
||||
private boolean assumeValidRestReferences;
|
||||
private boolean noExtensibleBindingMessages;
|
||||
private Locale locale;
|
||||
|
||||
private class AsteriskFilter implements FilenameFilter {
|
||||
|
@ -1272,6 +1273,7 @@ public class ValidationEngine implements IValidatorResourceFetcher {
|
|||
validator.setNoInvariantChecks(isNoInvariantChecks());
|
||||
validator.setValidationLanguage(language);
|
||||
validator.setAssumeValidRestReferences(assumeValidRestReferences);
|
||||
validator.setNoExtensibleWarnings(noExtensibleBindingMessages);
|
||||
validator.getContext().setLocale(locale);
|
||||
validator.setFetcher(this);
|
||||
return validator;
|
||||
|
@ -1701,6 +1703,14 @@ public class ValidationEngine implements IValidatorResourceFetcher {
|
|||
this.assumeValidRestReferences = assumeValidRestReferences;
|
||||
}
|
||||
|
||||
public boolean isNoExtensibleBindingMessages() {
|
||||
return noExtensibleBindingMessages;
|
||||
}
|
||||
|
||||
public void setNoExtensibleBindingMessages(boolean noExtensibleBindingMessages) {
|
||||
this.noExtensibleBindingMessages = noExtensibleBindingMessages;
|
||||
}
|
||||
|
||||
public byte[] transformVersion(String source, String targetVer, FhirFormat format, Boolean canDoNative) throws FHIRException, IOException, Exception {
|
||||
Content cnt = loadContent(source, "validate");
|
||||
org.hl7.fhir.r5.elementmodel.Element src = Manager.parse(context, new ByteArrayInputStream(cnt.focus), cnt.cntType);
|
||||
|
|
|
@ -32,6 +32,8 @@ public class CliContext {
|
|||
private String snomedCT = SnomedVersion.INTL.getCode();
|
||||
private String targetVer = null;
|
||||
private boolean doDebug = false;
|
||||
private boolean noInternalCaching = false; // internal, for when debugging terminology validation
|
||||
private boolean noExtensibleBindingMessages = false;
|
||||
private boolean assumeValidRestReferences = false;
|
||||
|
||||
public String getMap() {
|
||||
|
@ -286,4 +288,24 @@ public class CliContext {
|
|||
this.assumeValidRestReferences = assumeValidRestReferences;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isNoInternalCaching() {
|
||||
return noInternalCaching;
|
||||
}
|
||||
|
||||
public CliContext setNoInternalCaching(boolean noInternalCaching) {
|
||||
this.noInternalCaching = noInternalCaching;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isNoExtensibleBindingMessages() {
|
||||
return noExtensibleBindingMessages;
|
||||
}
|
||||
|
||||
public CliContext setNoExtensibleBindingMessages(boolean noExtensibleBindingMessages) {
|
||||
this.noExtensibleBindingMessages = noExtensibleBindingMessages;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -45,6 +45,8 @@ public class Params {
|
|||
public static final String DESTINATION = "-dest";
|
||||
public static final String LEFT = "-left";
|
||||
public static final String RIGHT = "-right";
|
||||
public static final String NO_INTERNAL_CACHING = "-no-internal-caching";
|
||||
public static final String NO_EXTENSIBLE_BINDING_WARNINGS = "-no-extensible-binding-warnings";
|
||||
|
||||
/**
|
||||
* Checks the list of passed in params to see if it contains the passed in param.
|
||||
|
@ -90,18 +92,19 @@ public class Params {
|
|||
i++; // ignore next parameter
|
||||
} else if (args[i].equals(PROFILE)) {
|
||||
String p = null;
|
||||
if (i + 1 == args.length)
|
||||
if (i + 1 == args.length) {
|
||||
throw new Error("Specified -profile without indicating profile source");
|
||||
else {
|
||||
} else {
|
||||
p = args[++i];
|
||||
cliContext.addProfile(args[++i]);
|
||||
cliContext.addProfile(args[i++]);
|
||||
}
|
||||
if (p != null && i + 1 < args.length && args[i + 1].equals("@")) {
|
||||
i++;
|
||||
if (i + 1 == args.length)
|
||||
if (i + 1 == args.length) {
|
||||
throw new Error("Specified -profile with @ without indicating profile location");
|
||||
else
|
||||
} else {
|
||||
cliContext.addLocation(p, args[++i]);
|
||||
}
|
||||
}
|
||||
} else if (args[i].equals(QUESTIONNAIRE)) {
|
||||
if (i + 1 == args.length)
|
||||
|
@ -126,6 +129,10 @@ public class Params {
|
|||
}
|
||||
} else if (args[i].equals(STRICT_EXTENSIONS)) {
|
||||
cliContext.setAnyExtensionsAllowed(false);
|
||||
} else if (args[i].equals(NO_INTERNAL_CACHING)) {
|
||||
cliContext.setNoInternalCaching(true);
|
||||
} else if (args[i].equals(NO_EXTENSIBLE_BINDING_WARNINGS)) {
|
||||
cliContext.setNoExtensibleBindingMessages(true);
|
||||
} else if (args[i].equals(HINT_ABOUT_NON_MUST_SUPPORT)) {
|
||||
cliContext.setHintAboutNonMustSupport(true);
|
||||
} else if (args[i].equals(TO_VERSION)) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.hl7.fhir.validation.cli;
|
||||
|
||||
import org.hl7.fhir.r5.context.TerminologyCache;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager;
|
||||
import org.hl7.fhir.r5.formats.IParser;
|
||||
import org.hl7.fhir.r5.formats.JsonParser;
|
||||
|
@ -155,6 +156,8 @@ public class ValidationUtils {
|
|||
validator.setLocale(cliContext.getLocale());
|
||||
validator.setSnomedExtension(cliContext.getSnomedCT());
|
||||
validator.setAssumeValidRestReferences(cliContext.isAssumeValidRestReferences());
|
||||
validator.setNoExtensibleBindingMessages(cliContext.isNoExtensibleBindingMessages());
|
||||
TerminologyCache.setNoCaching(cliContext.isNoInternalCaching());
|
||||
return validator;
|
||||
}
|
||||
|
||||
|
|
|
@ -139,6 +139,7 @@ import org.hl7.fhir.utilities.i18n.I18nConstants;
|
|||
import org.hl7.fhir.validation.BaseValidator;
|
||||
import org.hl7.fhir.validation.TimeTracker;
|
||||
import org.hl7.fhir.validation.instance.EnableWhenEvaluator.QStack;
|
||||
import org.hl7.fhir.validation.instance.type.CodeSystemValidator;
|
||||
import org.hl7.fhir.validation.instance.type.MeasureValidator;
|
||||
import org.hl7.fhir.validation.instance.type.QuestionnaireValidator;
|
||||
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
|
||||
|
@ -806,7 +807,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_VALUESET, system);
|
||||
// Lloyd: This error used to prohibit checking for downstream issues, but there are some cases where that checking needs to occur. Please talk to me before changing the code back.
|
||||
}
|
||||
hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system);
|
||||
boolean done = false;
|
||||
if (system.startsWith("https:") && system.length() > 7) {
|
||||
String ns = "http:"+system.substring(6);
|
||||
CodeSystem cs = getCodeSystem(ns);
|
||||
if (cs != null || Utilities.existsInList(system, "https://loinc.org", "https://unitsofmeasure.org", "https://snomed.info/sct", "https://www.nlm.nih.gov/research/umls/rxnorm")) {
|
||||
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_HTTPS, system);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
hint(errors, IssueType.UNKNOWN, element.line(), element.col(), path, done, I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, system);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return true;
|
||||
|
@ -1613,6 +1623,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
if ("http://hl7.org/fhir/StructureDefinition/regex".equals(extUrl)) {
|
||||
list.get(1).setExpression("ElementDefinition.type");
|
||||
}
|
||||
if ("http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version".equals(extUrl)) {
|
||||
list.get(0).setExpression("Element"); // well, it can't be used anywhere but the list of places it can be used is quite long
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -3466,7 +3479,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
} else if (element.getType().equals("CapabilityStatement")) {
|
||||
validateCapabilityStatement(errors, element, stack);
|
||||
} else if (element.getType().equals("CodeSystem")) {
|
||||
validateCodeSystem(errors, element, stack);
|
||||
new CodeSystemValidator(context, timeTracker).validateCodeSystem(errors, element, stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3550,28 +3563,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
}
|
||||
|
||||
private void validateCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack) {
|
||||
String url = cs.getNamedChildValue("url");
|
||||
String vsu = cs.getNamedChildValue("valueSet");
|
||||
if (!Utilities.noString(vsu)) {
|
||||
ValueSet vs;
|
||||
try {
|
||||
vs = context.fetchResourceWithException(ValueSet.class, vsu);
|
||||
} catch (FHIRException e) {
|
||||
vs = null;
|
||||
}
|
||||
if (vs != null) {
|
||||
if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(), I18nConstants.CODESYSTEM_CS_VS_MISMATCH, url, vsu))
|
||||
if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1, I18nConstants.CODESYSTEM_CS_VS_INVALID, url, vsu))
|
||||
if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url), I18nConstants.CODESYSTEM_CS_VS_WRONGSYSTEM, url, vsu, vs.getCompose().getInclude().get(0).getSystem())) {
|
||||
rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet()
|
||||
&& !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), I18nConstants.CODESYSTEM_CS_VS_INCLUDEDETAILS, url, vsu);
|
||||
}
|
||||
}
|
||||
} // todo... try getting the value set the other way...
|
||||
}
|
||||
|
||||
|
||||
private void validateBundle(List<ValidationMessage> errors, Element bundle, NodeStack stack, boolean checkSpecials) {
|
||||
List<Element> entries = new ArrayList<Element>();
|
||||
bundle.getNamedChildren(ENTRY, entries);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package org.hl7.fhir.validation.instance.type;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.i18n.I18nConstants;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
|
||||
import org.hl7.fhir.validation.BaseValidator;
|
||||
import org.hl7.fhir.validation.TimeTracker;
|
||||
import org.hl7.fhir.validation.instance.utils.NodeStack;
|
||||
|
||||
public class CodeSystemValidator extends BaseValidator {
|
||||
|
||||
public CodeSystemValidator(IWorkerContext context, TimeTracker timeTracker) {
|
||||
super(context);
|
||||
source = Source.InstanceValidator;
|
||||
this.timeTracker = timeTracker;
|
||||
}
|
||||
|
||||
public void validateCodeSystem(List<ValidationMessage> errors, Element cs, NodeStack stack) {
|
||||
String url = cs.getNamedChildValue("url");
|
||||
String content = cs.getNamedChildValue("content");
|
||||
|
||||
String vsu = cs.getNamedChildValue("valueSet");
|
||||
if (!Utilities.noString(vsu)) {
|
||||
hint(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), "complete".equals(content), I18nConstants.CODESYSTEM_CS_NO_VS_NOTCOMPLETE);
|
||||
ValueSet vs;
|
||||
try {
|
||||
vs = context.fetchResourceWithException(ValueSet.class, vsu);
|
||||
} catch (FHIRException e) {
|
||||
vs = null;
|
||||
}
|
||||
if (vs != null) {
|
||||
if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.hasCompose() && !vs.hasExpansion(), I18nConstants.CODESYSTEM_CS_VS_MISMATCH, url, vsu))
|
||||
if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1, I18nConstants.CODESYSTEM_CS_VS_INVALID, url, vsu))
|
||||
if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url), I18nConstants.CODESYSTEM_CS_VS_WRONGSYSTEM, url, vsu, vs.getCompose().getInclude().get(0).getSystem())) {
|
||||
rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet()
|
||||
&& !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), I18nConstants.CODESYSTEM_CS_VS_INCLUDEDETAILS, url, vsu);
|
||||
}
|
||||
}
|
||||
} // todo... try getting the value set the other way...
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -3,10 +3,13 @@ package org.hl7.fhir.validation.tests;
|
|||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.hl7.fhir.convertors.R5ToR5Loader;
|
||||
import org.hl7.fhir.r5.conformance.ProfileComparer;
|
||||
import org.hl7.fhir.r5.model.FhirPublication;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.utils.KeyGenerator;
|
||||
import org.hl7.fhir.utilities.cache.PackageCacheManager;
|
||||
import org.hl7.fhir.utilities.cache.ToolsVersion;
|
||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||
import org.hl7.fhir.validation.ValidationEngine;
|
||||
import org.hl7.fhir.validation.tests.utilities.TestUtilities;
|
||||
|
@ -22,7 +25,8 @@ public class ProfileComparisonTests {
|
|||
System.out.println("Compare US Patient Core with AU Patient Base");
|
||||
ValidationEngine ve = new ValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, null, FhirPublication.STU3, "3.0.2");
|
||||
ve.loadIg("hl7.fhir.us.core#1.0.1", false);
|
||||
ve.loadIg("hl7.fhir.au.base#dev", false);
|
||||
ve.loadIg("hl7.fhir.au.base#current", false);
|
||||
ve.getContext().loadFromPackage(new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION).loadPackage("hl7.fhir.pubpack", "0.0.4"), new R5ToR5Loader(new String[] {"Binary"}), "Binary");
|
||||
|
||||
|
||||
String left = "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient";
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -13,7 +13,7 @@
|
|||
each other. It is fine to bump the point version of this POM without affecting
|
||||
HAPI FHIR.
|
||||
-->
|
||||
<version>4.2.18-SNAPSHOT</version>
|
||||
<version>4.2.19-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<hapi_fhir_version>4.2.0</hapi_fhir_version>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@echo off
|
||||
|
||||
set oldver=4.2.17
|
||||
set newver=4.2.18
|
||||
set oldver=4.2.18
|
||||
set newver=4.2.19
|
||||
|
||||
echo ..
|
||||
echo =========================================================================
|
||||
|
|
Loading…
Reference in New Issue