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}