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