001package org.hl7.fhir.r5.renderers; 002 003import java.io.IOException; 004import java.io.UnsupportedEncodingException; 005import java.util.ArrayList; 006import java.util.List; 007import java.util.Map; 008 009import org.hl7.fhir.exceptions.DefinitionException; 010import org.hl7.fhir.exceptions.FHIRFormatError; 011import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; 012import org.hl7.fhir.r5.model.CanonicalResource; 013import org.hl7.fhir.r5.model.CodeSystem; 014import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; 015import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent; 016import org.hl7.fhir.r5.model.ConceptMap; 017import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; 018import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent; 019import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; 020import org.hl7.fhir.r5.model.Questionnaire; 021import org.hl7.fhir.r5.model.Resource; 022import org.hl7.fhir.r5.model.StructureDefinition; 023import org.hl7.fhir.r5.model.ValueSet; 024import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; 025import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; 026import org.hl7.fhir.r5.renderers.utils.RenderingContext; 027import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; 028import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; 029import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; 030import org.hl7.fhir.r5.utils.ToolingExtensions; 031import org.hl7.fhir.utilities.Utilities; 032import org.hl7.fhir.utilities.xhtml.XhtmlNode; 033 034public abstract class TerminologyRenderer extends ResourceRenderer { 035 036 private static final boolean DEBUG = false; 037 038 039 public TerminologyRenderer(RenderingContext context) { 040 super(context); 041 } 042 043 public TerminologyRenderer(RenderingContext context, ResourceContext rcontext) { 044 super(context, rcontext); 045 } 046 047 public String display(Resource r) throws UnsupportedEncodingException, IOException { 048 return ((CanonicalResource) r).present(); 049 } 050 051 public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException { 052 if (r.has("title")) { 053 return r.children("title").get(0).getBase().primitiveValue(); 054 } 055 if (r.has("name")) { 056 return r.children("name").get(0).getBase().primitiveValue(); 057 } 058 return "??"; 059 } 060 061 protected class TargetElementComponentWrapper { 062 protected ConceptMapGroupComponent group; 063 protected TargetElementComponent comp; 064 protected TargetElementComponentWrapper(ConceptMapGroupComponent group, TargetElementComponent comp) { 065 super(); 066 this.group = group; 067 this.comp = comp; 068 } 069 070 } 071 072 public class UsedConceptMap { 073 074 private ConceptMapRenderInstructions details; 075 private String link; 076 private ConceptMap map; 077 public UsedConceptMap(ConceptMapRenderInstructions details, String link, ConceptMap map) { 078 super(); 079 this.details = details; 080 this.link = link; 081 this.map = map; 082 } 083 public ConceptMapRenderInstructions getDetails() { 084 return details; 085 } 086 public ConceptMap getMap() { 087 return map; 088 } 089 public String getLink() { 090 return link; 091 } 092 } 093 094 public class ConceptMapRenderInstructions { 095 private String name; 096 private String url; 097 private boolean doDescription; 098 public ConceptMapRenderInstructions(String name, String url, boolean doDescription) { 099 super(); 100 this.name = name; 101 this.url = url; 102 this.doDescription = doDescription; 103 } 104 public String getName() { 105 return name; 106 } 107 public String getUrl() { 108 return url; 109 } 110 public boolean isDoDescription() { 111 return doDescription; 112 } 113 } 114 115 116 protected XhtmlNode addMapHeaders(XhtmlNode tr, List<UsedConceptMap> maps) throws FHIRFormatError, DefinitionException, IOException { 117 for (UsedConceptMap m : maps) { 118 XhtmlNode td = tr.td(); 119 XhtmlNode b = td.b(); 120 String link = m.getLink(); 121 XhtmlNode a = b.ah(link); 122 a.addText(m.getDetails().getName()); 123 if (m.getDetails().isDoDescription() && m.getMap().hasDescription()) 124 addMarkdown(td, m.getMap().getDescription()); 125 } 126 return tr; 127 } 128 129 protected String getHeader() { 130 int i = 3; 131 while (i <= getContext().getHeaderLevelContext()) 132 i++; 133 if (i > 6) 134 i = 6; 135 return "h"+Integer.toString(i); 136 } 137 138 protected List<TargetElementComponentWrapper> findMappingsForCode(String code, ConceptMap map) { 139 List<TargetElementComponentWrapper> mappings = new ArrayList<TargetElementComponentWrapper>(); 140 141 for (ConceptMapGroupComponent g : map.getGroup()) { 142 for (SourceElementComponent c : g.getElement()) { 143 if (c.getCode().equals(code)) 144 for (TargetElementComponent cc : c.getTarget()) 145 mappings.add(new TargetElementComponentWrapper(g, cc)); 146 } 147 } 148 return mappings; 149 } 150 151 152 153 protected String getCharForRelationship(TargetElementComponent mapping) { 154 if (!mapping.hasRelationship()) 155 return ""; 156 switch (mapping.getRelationship()) { 157 case EQUIVALENT : return "~"; 158 case SOURCEISNARROWERTHANTARGET : return "<"; 159 case SOURCEISBROADERTHANTARGET : return ">"; 160 case NOTRELATEDTO : return "!="; 161 default: return "?"; 162 } 163 } 164 165 protected <T extends Resource> void addCsRef(ConceptSetComponent inc, XhtmlNode li, T cs) { 166 String ref = null; 167 boolean addHtml = true; 168 if (cs != null) { 169 ref = (String) cs.getUserData("external.url"); 170 if (Utilities.noString(ref)) 171 ref = (String) cs.getUserData("filename"); 172 else 173 addHtml = false; 174 if (Utilities.noString(ref)) { 175 ref = (String) cs.getWebPath(); 176 if (ref != null) { 177 addHtml = false; 178 } 179 } 180 } 181 String spec = getSpecialReference(inc.getSystem()); 182 if (spec != null) { 183 XhtmlNode a = li.ah(spec); 184 a.code(inc.getSystem()); 185 } else if (cs != null && ref != null) { 186 if (addHtml && !ref.contains(".html")) 187 ref = ref + ".html"; 188 ref = context.fixReference(ref); 189 XhtmlNode a = li.ah(ref.replace("\\", "/")); 190 a.code(inc.getSystem()); 191 } else { 192 li.code(inc.getSystem()); 193 } 194 } 195 196 197 private String getSpecialReference(String system) { 198 if ("http://snomed.info/sct".equals(system)) 199 return "http://www.snomed.org/"; 200 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", 201 "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", 202 "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", 203 "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/")) 204 return system; 205 206 return null; 207 } 208 209 protected XhtmlNode addTableHeaderRowStandard(XhtmlNode t, boolean hasHierarchy, boolean hasDisplay, boolean definitions, boolean comments, boolean version, boolean deprecated, List<PropertyComponent> properties, List<String> langs, Map<String, String> designations, boolean doDesignations) { 210 XhtmlNode tr = t.tr(); 211 if (hasHierarchy) { 212 tr.td().b().tx(context.formatPhrase(RenderingContext.TERMINOLOGY_LVL)); 213 } 214 tr.td().attribute("style", "white-space:nowrap").b().tx(formatPhrase(RenderingContext.GENERAL_CODE)); 215 if (hasDisplay) { 216 tr.td().b().tx(formatPhrase(RenderingContext.TX_DISPLAY)); 217 } 218 if (definitions) { 219 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_DEFINITION)); 220 } 221 if (deprecated) { 222 tr.td().b().tx(formatPhrase(RenderingContext.CODESYSTEM_DEPRECATED)); 223 } 224 if (comments) { 225 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_COMMENTS)); 226 } 227 if (version) { 228 tr.td().b().tx(formatPhrase(RenderingContext.GENERAL_VER)); 229 } 230 if (properties != null) { 231 for (PropertyComponent pc : properties) { 232 String display = getDisplayForProperty(pc); 233 tr.td().b().tx(display); 234 } 235 } 236 if (doDesignations) { 237 if (designations != null) { 238 for (String url : designations.keySet()) { 239 tr.td().b().addText(designations.get(url)); 240 } 241 } 242 if (langs != null) { 243 for (String lang : langs) { 244 tr.td().b().addText(describeLang(lang)); 245 } 246 } 247 } 248 return tr; 249 } 250 251 protected String getDisplayForProperty(PropertyComponent pc) { 252 String display = ToolingExtensions.getPresentation(pc, pc.getCodeElement()); 253 if (display == null || display.equals(pc.getCode()) && pc.hasUri()) { 254 display = getDisplayForProperty(pc.getUri()); 255 if (display == null) { 256 display = pc.getCode(); 257 } 258 } 259 return display; 260 } 261 262 263 protected String getDisplayForProperty(String uri) { 264 if (Utilities.noString(uri)){ 265 return null; 266 } 267 String code = null; 268 if (uri.contains("#")) { 269 code = uri.substring(uri.indexOf("#")+1); 270 uri = uri.substring(0, uri.indexOf("#")); 271 } 272 CodeSystem cs = getContext().getWorker().fetchCodeSystem(uri); 273 if (cs == null) { 274 return null; 275 } 276 ConceptDefinitionComponent cc = code == null ? null : CodeSystemUtilities.getCode(cs, code); 277 return cc == null ? null : cc.getDisplay(); 278 } 279 280 281 protected void AddVsRef(String value, XhtmlNode li, Resource source) { 282 Resource res = null; 283 if (rcontext != null) { 284 BundleEntryComponent be = rcontext.resolve(value); 285 if (be != null) { 286 res = be.getResource(); 287 } 288 } 289 if (res != null && !(res instanceof CanonicalResource)) { 290 li.addText(value); 291 return; 292 } 293 CanonicalResource vs = (CanonicalResource) res; 294 if (vs == null) 295 vs = getContext().getWorker().findTxResource(ValueSet.class, value, source); 296 if (vs == null) 297 vs = getContext().getWorker().fetchResource(StructureDefinition.class, value, source); 298 if (vs == null) 299 vs = getContext().getWorker().fetchResource(Questionnaire.class, value, source); 300 if (vs != null) { 301 String ref = (String) vs.getWebPath(); 302 303 ref = context.fixReference(ref); 304 XhtmlNode a = li.ah(ref == null ? "?ngen-11?" : ref.replace("\\", "/")); 305 a.addText(vs.present()); 306 } else { 307 CodeSystem cs = getContext().getWorker().fetchCodeSystem(value); 308 if (cs != null) { 309 String ref = (String) cs.getWebPath(); 310 ref = context.fixReference(ref); 311 XhtmlNode a = li.ah(ref == null ? "?ngen-12?" : ref.replace("\\", "/")); 312 a.addText(value); 313 } else if (value.equals("http://snomed.info/sct") || value.equals("http://snomed.info/id")) { 314 XhtmlNode a = li.ah(value); 315 a.tx(context.formatPhrase(RenderingContext.STRUC_DEF_SNOMED)); 316 } 317 else { 318 if (value.startsWith("http://hl7.org") && !Utilities.existsInList(value, "http://hl7.org/fhir/sid/icd-10-us")) { 319 if (DEBUG) { 320 System.out.println("Unable to resolve value set "+value); 321 } 322 } 323 li.addText(value); 324 } 325 } 326 } 327 328 protected String getDisplayForConcept(String system, String version, String value) { 329 if (value == null || system == null) 330 return null; 331 ValidationResult cl = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions().withVersionFlexible(true), system, version, value, null); 332 return cl == null ? null : cl.getDisplay(); 333 } 334 335 336 protected void clipboard(XhtmlNode x, String img, String title, String source) { 337 XhtmlNode span = x.span("cursor: pointer", formatPhrase(RenderingContext.TERM_REND_COPY, title)); 338 span.attribute("onClick", "navigator.clipboard.writeText('"+Utilities.escapeJson(source)+"');"); 339 span.img(img, "btn").setAttribute("width", "24px").setAttribute("height", "16px"); 340 } 341 342 343 344}