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