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.context.IWorkerContext.ValidationResult; 012import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; 013import org.hl7.fhir.r5.model.CanonicalResource; 014import org.hl7.fhir.r5.model.CodeSystem; 015import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; 016import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent; 017import org.hl7.fhir.r5.model.ConceptMap; 018import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; 019import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent; 020import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; 021import org.hl7.fhir.r5.model.Questionnaire; 022import org.hl7.fhir.r5.model.Resource; 023import org.hl7.fhir.r5.model.StructureDefinition; 024import org.hl7.fhir.r5.model.ValueSet; 025import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; 026import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; 027import org.hl7.fhir.r5.renderers.utils.RenderingContext; 028import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; 029import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; 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("Lvl"); 213 } 214 tr.td().attribute("style", "white-space:nowrap").b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Code", getContext().getLang())); 215 if (hasDisplay) { 216 tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Display", getContext().getLang())); 217 } 218 if (definitions) { 219 tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Definition", getContext().getLang())); 220 } 221 if (deprecated) { 222 tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Deprecated", getContext().getLang())); 223 } 224 if (comments) { 225 tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Comments", getContext().getLang())); 226 } 227 if (version) { 228 tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Version", getContext().getLang())); 229 } 230 if (properties != null) { 231 for (PropertyComponent pc : properties) { 232 String display = ToolingExtensions.getPresentation(pc, pc.getCodeElement()); 233 if (display == null || display.equals(pc.getCode()) && pc.hasUri()) { 234 display = getDisplayForProperty(pc.getUri()); 235 if (display == null) { 236 display = pc.getCode(); 237 } 238 } 239 tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", display, getContext().getLang())); 240 } 241 } 242 if (doDesignations) { 243 if (designations != null) { 244 for (String url : designations.keySet()) { 245 tr.td().b().addText(designations.get(url)); 246 } 247 } 248 if (langs != null) { 249 for (String lang : langs) { 250 tr.td().b().addText(describeLang(lang)); 251 } 252 } 253 } 254 return tr; 255 } 256 257 258 protected String getDisplayForProperty(String uri) { 259 if (Utilities.noString(uri)){ 260 return null; 261 } 262 String code = null; 263 if (uri.contains("#")) { 264 code = uri.substring(uri.indexOf("#")+1); 265 uri = uri.substring(0, uri.indexOf("#")); 266 } 267 CodeSystem cs = getContext().getWorker().fetchCodeSystem(uri); 268 if (cs == null) { 269 return null; 270 } 271 ConceptDefinitionComponent cc = code == null ? null : CodeSystemUtilities.getCode(cs, code); 272 return cc == null ? null : cc.getDisplay(); 273 } 274 275 276 protected void AddVsRef(String value, XhtmlNode li, Resource source) { 277 Resource res = null; 278 if (rcontext != null) { 279 BundleEntryComponent be = rcontext.resolve(value); 280 if (be != null) { 281 res = be.getResource(); 282 } 283 } 284 if (res != null && !(res instanceof CanonicalResource)) { 285 li.addText(value); 286 return; 287 } 288 CanonicalResource vs = (CanonicalResource) res; 289 if (vs == null) 290 vs = getContext().getWorker().fetchResource(ValueSet.class, value, source); 291 if (vs == null) 292 vs = getContext().getWorker().fetchResource(StructureDefinition.class, value, source); 293 if (vs == null) 294 vs = getContext().getWorker().fetchResource(Questionnaire.class, value, source); 295 if (vs != null) { 296 String ref = (String) vs.getWebPath(); 297 298 ref = context.fixReference(ref); 299 XhtmlNode a = li.ah(ref == null ? "?ngen-11?" : ref.replace("\\", "/")); 300 a.addText(vs.present()); 301 } else { 302 CodeSystem cs = getContext().getWorker().fetchCodeSystem(value); 303 if (cs != null) { 304 String ref = (String) cs.getWebPath(); 305 ref = context.fixReference(ref); 306 XhtmlNode a = li.ah(ref == null ? "?ngen-12?" : ref.replace("\\", "/")); 307 a.addText(value); 308 } else if (value.equals("http://snomed.info/sct") || value.equals("http://snomed.info/id")) { 309 XhtmlNode a = li.ah(value); 310 a.tx("SNOMED-CT"); 311 } 312 else { 313 if (value.startsWith("http://hl7.org") && !Utilities.existsInList(value, "http://hl7.org/fhir/sid/icd-10-us")) { 314 if (DEBUG) { 315 System.out.println("Unable to resolve value set "+value); 316 } 317 } 318 li.addText(value); 319 } 320 } 321 } 322 323 protected String getDisplayForConcept(String system, String version, String value) { 324 if (value == null || system == null) 325 return null; 326 ValidationResult cl = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions().withVersionFlexible(true), system, version, value, null); 327 return cl == null ? null : cl.getDisplay(); 328 } 329 330 331 protected void clipboard(XhtmlNode x, String img, String title, String source) { 332 XhtmlNode span = x.span("cursor: pointer", "Copy "+title+" Format to clipboard"); 333 span.attribute("onClick", "navigator.clipboard.writeText('"+Utilities.escapeJson(source)+"');"); 334 span.img(img, "btn").setAttribute("width", "24px").setAttribute("height", "16px"); 335 } 336 337 338 339}