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