001package org.hl7.fhir.r5.renderers;
002
003import java.io.IOException;
004import java.util.ArrayList;
005import java.util.Collections;
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.CodeSystem;
012import org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode;
013import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
014import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
015import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
016import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
017import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
018import org.hl7.fhir.r5.model.Coding;
019import org.hl7.fhir.r5.model.Enumeration;
020import org.hl7.fhir.r5.model.Extension;
021import org.hl7.fhir.r5.model.Resource;
022import org.hl7.fhir.r5.renderers.utils.RenderingContext;
023import org.hl7.fhir.r5.renderers.utils.RenderingContext.KnownLinkType;
024import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
025import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
026import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.CodeSystemNavigator;
027import org.hl7.fhir.r5.utils.ToolingExtensions;
028import org.hl7.fhir.utilities.LoincLinker;
029import org.hl7.fhir.utilities.Utilities;
030import org.hl7.fhir.utilities.xhtml.XhtmlNode;
031
032public class CodeSystemRenderer extends TerminologyRenderer {
033
034  public CodeSystemRenderer(RenderingContext context) {
035    super(context);
036  }
037
038  public CodeSystemRenderer(RenderingContext context, ResourceContext rcontext) {
039    super(context, rcontext);
040  }
041  
042
043  public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException {
044    return render(x, (CodeSystem) dr);
045  }
046  
047  public boolean render(XhtmlNode x, CodeSystem cs) throws FHIRFormatError, DefinitionException, IOException {
048    boolean hasExtensions = false;
049
050    if (context.isHeader()) {
051      XhtmlNode h = x.h2();
052      h.addText(cs.hasTitle() ? cs.getTitle() : cs.getName());
053      addMarkdown(x, cs.getDescription());
054      if (cs.hasCopyright())
055        generateCopyright(x, cs );
056    }
057
058    boolean props = generateProperties(x, cs);
059    generateFilters(x, cs);
060    List<UsedConceptMap> maps = new ArrayList<UsedConceptMap>();
061    hasExtensions = generateCodeSystemContent(x, cs, hasExtensions, maps, props);
062
063    return hasExtensions;
064  }
065
066  public void describe(XhtmlNode x, CodeSystem cs) {
067    x.tx(display(cs));
068  }
069
070  public String display(CodeSystem cs) {
071    return cs.present();
072  }
073  
074  private void generateFilters(XhtmlNode x, CodeSystem cs) {
075    if (cs.hasFilter()) {
076      x.para().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Filters", getContext().getLang()));
077      XhtmlNode tbl = x.table("grid");
078      XhtmlNode tr = tbl.tr();
079      tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Code", getContext().getLang()));
080      tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Description", getContext().getLang()));
081      tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "operator", getContext().getLang()));
082      tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Value", getContext().getLang()));
083      for (CodeSystemFilterComponent f : cs.getFilter()) {
084        tr = tbl.tr();
085        tr.td().tx(f.getCode());
086        tr.td().tx(f.getDescription());
087        XhtmlNode td = tr.td();
088        for (Enumeration<org.hl7.fhir.r5.model.Enumerations.FilterOperator> t : f.getOperator())
089          td.tx(t.asStringValue()+" ");
090        tr.td().tx(f.getValue());
091      }
092    }
093  }
094
095  private boolean generateProperties(XhtmlNode x, CodeSystem cs) {
096    if (cs.hasProperty()) {
097      boolean hasRendered = false;
098      boolean hasURI = false;
099      boolean hasDescription = false;
100      for (PropertyComponent p : cs.getProperty()) {
101        hasRendered = hasRendered || !p.getCode().equals(ToolingExtensions.getPresentation(p, p.getCodeElement()));
102        hasURI = hasURI || p.hasUri();
103        hasDescription = hasDescription || p.hasDescription();
104      }
105      
106      x.para().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Properties", getContext().getLang()));
107      x.para().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "This code system  defines the following properties for its concepts", getContext().getLang()));
108      XhtmlNode tbl = x.table("grid");
109      XhtmlNode tr = tbl.tr();
110      if (hasRendered) {
111        tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Name", getContext().getLang()));        
112      }
113      tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Code", getContext().getLang()));
114      if (hasURI) {
115        tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "URI", getContext().getLang()));
116      }
117      tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Type", getContext().getLang()));
118      if (hasDescription) {
119        tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Description", getContext().getLang()));
120      }
121      for (PropertyComponent p : cs.getProperty()) {
122        tr = tbl.tr();
123        if (hasRendered) {
124          tr.td().tx(ToolingExtensions.getPresentation(p, p.getCodeElement()));          
125        }
126        tr.td().tx(p.getCode());
127        if (hasURI) {
128          tr.td().tx(p.getUri());
129        }
130        tr.td().tx(p.hasType() ? p.getType().toCode() : "");
131        if (hasDescription) {
132          tr.td().tx(p.getDescription());
133        }
134      }
135      return true;
136    } else {
137      return false;
138    }
139  }
140
141  private boolean generateCodeSystemContent(XhtmlNode x, CodeSystem cs, boolean hasExtensions, List<UsedConceptMap> maps, boolean props) throws FHIRFormatError, DefinitionException, IOException {
142    if (props) {
143      x.para().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Concepts", getContext().getLang()));
144    }
145    XhtmlNode p = x.para();
146    p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system "));
147    p.code().tx(cs.getUrl());
148    if (cs.getContent() == CodeSystemContentMode.COMPLETE)
149      p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines the following codes")+":");
150    else if (cs.getContent() == CodeSystemContentMode.EXAMPLE)
151      p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines some example codes")+":");
152    else if (cs.getContent() == CodeSystemContentMode.FRAGMENT )
153      p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines many codes, of which the following are a subset")+":");
154    else if (cs.getContent() == CodeSystemContentMode.NOTPRESENT ) {
155      p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), " defines many codes, but they are not represented here"));
156      return false;
157    }
158    XhtmlNode t = x.table( "codes");
159    boolean definitions = false;
160    boolean commentS = false;
161    boolean deprecated = false;
162    boolean display = false;
163    boolean hierarchy = false;
164    boolean version = false;
165    boolean ignoreStatus = false;
166    boolean isSupplement = cs.getContent() == CodeSystemContentMode.SUPPLEMENT;
167    List<PropertyComponent> properties = new ArrayList<>();
168    for (PropertyComponent cp : cs.getProperty()) {
169      if (showPropertyInTable(cp)) {
170        boolean exists = false;
171        for (ConceptDefinitionComponent c : cs.getConcept()) {
172          exists = exists || conceptsHaveProperty(c, cp);
173        }
174        if (exists) {
175          properties.add(cp);
176          if ("status".equals(cp.getCode())) {
177            ignoreStatus = true;
178          }
179        }
180      }
181    }
182    for (ConceptDefinitionComponent c : cs.getConcept()) {
183      commentS = commentS || conceptsHaveComments(c);
184      deprecated = deprecated || conceptsHaveDeprecated(cs, c, ignoreStatus);
185      display = display || conceptsHaveDisplay(c);
186      version = version || conceptsHaveVersion(c);
187      hierarchy = hierarchy || c.hasConcept();
188      definitions = definitions || conceptsHaveDefinition(c);
189    }
190    CodeSystemNavigator csNav = new CodeSystemNavigator(cs);
191    hierarchy = hierarchy || csNav.isRestructure();
192    
193    List<String> langs = new ArrayList<>();
194    addCopyColumn(addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, definitions, commentS, version, deprecated, properties, null, null, false), maps));
195    for (ConceptDefinitionComponent c : csNav.getConcepts(null)) {
196      hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, definitions, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs, isSupplement) || hasExtensions;
197    }
198    if (langs.size() > 0) {
199      Collections.sort(langs);
200      x.para().b().tx("Additional Language Displays");
201      t = x.table( "codes");
202      XhtmlNode tr = t.tr();
203      tr.td().b().tx("Code");
204      for (String lang : langs)
205        tr.td().b().addText(describeLang(lang));
206      for (ConceptDefinitionComponent c : cs.getConcept()) {
207        addLanguageRow(c, t, langs);
208      }
209    }
210    return hasExtensions;
211  }
212
213  private void addCopyColumn(XhtmlNode tr) {
214    if (context.isCopyButton()) {
215      tr.td().b().tx("Copy");
216    }
217    
218  }
219
220  private boolean conceptsHaveDefinition(ConceptDefinitionComponent c) {
221    if (c.hasDefinition()) {
222      return true;
223    }
224    for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { 
225      if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) {
226        return true;
227      }
228    }
229    for (ConceptDefinitionComponent g : c.getConcept()) {
230      if (conceptsHaveDefinition(g)) {
231        return true;
232      }
233    }
234    return false;
235  }
236
237  private boolean conceptsHaveProperty(ConceptDefinitionComponent c, PropertyComponent cp) {
238    if (CodeSystemUtilities.hasProperty(c, cp.getCode()))
239      return true;
240    for (ConceptDefinitionComponent g : c.getConcept())
241      if (conceptsHaveProperty(g,  cp))
242        return true;
243    return false;
244
245  }
246
247  private boolean showPropertyInTable(PropertyComponent cp) {
248    if (cp.hasCode()) {
249      if (cp.hasExtension(ToolingExtensions.EXT_RENDERED_VALUE)) {
250        return true;
251      }
252      if (cp.getCodeElement().hasExtension(ToolingExtensions.EXT_RENDERED_VALUE)) {
253        return true;
254      }
255      String uri = cp.getUri();
256      if (Utilities.noString(uri)){
257        return true; // do we always want to render properties in this case? Not sure...
258      }
259      String code = null;
260      if (uri.contains("#")) {
261        code = uri.substring(uri.indexOf("#")+1);
262        uri = uri.substring(0, uri.indexOf("#"));
263      }
264      if (Utilities.existsInList(uri, "http://hl7.org/fhir/concept-properties") || context.getCodeSystemPropList().contains(uri)) {
265        return true;
266      };
267      CodeSystem cs = getContext().getWorker().fetchCodeSystem(uri);
268      if (cs == null) {
269        return false;
270      }
271      return code == null ? false : CodeSystemUtilities.hasCode(cs, code);
272    }
273    return false;
274  }
275
276
277  private int countConcepts(List<ConceptDefinitionComponent> list) {
278    int count = list.size();
279    for (ConceptDefinitionComponent c : list)
280      if (c.hasConcept())
281        count = count + countConcepts(c.getConcept());
282    return count;
283  }
284  
285  private boolean conceptsHaveComments(ConceptDefinitionComponent c) {
286    if (ToolingExtensions.hasCSComment(c))
287      return true;
288    for (ConceptDefinitionComponent g : c.getConcept())
289      if (conceptsHaveComments(g))
290        return true;
291    return false;
292  }
293
294  private boolean conceptsHaveDisplay(ConceptDefinitionComponent c) {
295    if (c.hasDisplay() && !c.getDisplay().equals(c.getCode()))
296      return true;
297    for (ConceptDefinitionComponent g : c.getConcept())
298      if (conceptsHaveDisplay(g))
299        return true;
300    return false;
301  }
302
303  private boolean conceptsHaveVersion(ConceptDefinitionComponent c) {
304    if (c.hasUserData("cs.version.notes"))
305      return true;
306    for (ConceptDefinitionComponent g : c.getConcept())
307      if (conceptsHaveVersion(g))
308        return true;
309    return false;
310  }
311
312  private boolean conceptsHaveDeprecated(CodeSystem cs, ConceptDefinitionComponent c, boolean ignoreStatus) {
313    if (CodeSystemUtilities.isDeprecated(cs, c, ignoreStatus))
314      return true;
315    for (ConceptDefinitionComponent g : c.getConcept())
316      if (conceptsHaveDeprecated(cs, g, ignoreStatus))
317        return true;
318    return false;
319  }
320
321
322
323  private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, boolean hasDefinitions, boolean comment, boolean version, boolean deprecated, List<UsedConceptMap> maps, String system, CodeSystem cs, List<PropertyComponent> properties, CodeSystemNavigator csNav, List<String> langs, boolean isSupplement) throws FHIRFormatError, DefinitionException, IOException {
324    boolean hasExtensions = false;
325    XhtmlNode tr = t.tr();
326    boolean notCurrent = CodeSystemUtilities.isNotCurrent(cs, c);
327    if (notCurrent) {
328      tr.setAttribute("style", "background-color: #ffeeee");
329    }
330    
331    XhtmlNode td = tr.td();
332    if (hasHierarchy) {
333      td.addText(Integer.toString(level+1));
334      td = tr.td();
335      String s = Utilities.padLeft("", '\u00A0', level*2);
336      td.addText(s);
337    }
338    String link = isSupplement ? getLinkForCode(cs.getSupplements(), null, c.getCode()) : null;
339    if (link != null) {
340      td.ah(link).attribute("style", "white-space:nowrap").addText(c.getCode());
341    } else {
342      td.attribute("style", "white-space:nowrap").addText(c.getCode());
343    }      
344    XhtmlNode a;
345    if (c.hasCodeElement()) {
346      td.an(cs.getId()+"-" + Utilities.nmtokenize(c.getCode()));
347    }
348
349    for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
350      if (cd.hasLanguage() && !langs.contains(cd.getLanguage()) && (!cs.hasLanguage() || !cs.getLanguage().equals(cd.getLanguage()))) {
351        langs.add(cd.getLanguage());
352      }
353        
354    }
355
356    if (hasDisplay) {
357      td = tr.td();
358      renderDisplayName(c, cs, td);
359    } 
360    if (hasDefinitions) {
361      td = tr.td();
362      if (c != null && 
363          c.hasDefinitionElement()) {
364        if (getContext().getLang() == null) {
365          if (hasMarkdownInDefinitions(cs))
366            addMarkdown(td, c.getDefinition());
367          else
368            td.addText(c.getDefinition());
369        } else if (getContext().getLang().equals("*")) {
370          boolean sl = false;
371          for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) 
372            if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) 
373              sl = true;
374          td.addText((sl ? cs.getLanguage("en")+": " : "")+c.getDefinition());
375          for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
376            if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) {
377              td.br();
378              td.addText(cd.getLanguage()+": "+cd.getValue());
379            }
380          }
381        } else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) {
382          td.addText(c.getDefinition());
383        } else {
384          for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
385            if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && cd.getLanguage().equals(getContext().getLang())) {
386              td.addText(cd.getValue());
387            }
388          }
389        }
390      }
391    }
392    if (deprecated) {
393      td = tr.td();
394      Boolean b = CodeSystemUtilities.isDeprecated(cs, c, false);
395      if (b !=  null && b) {
396        smartAddText(td, getContext().getWorker().translator().translate("xhtml-gen-cs", "Deprecated", getContext().getLang()));
397        hasExtensions = true;
398        if (ToolingExtensions.hasExtension(c, ToolingExtensions.EXT_REPLACED_BY)) {
399          Coding cc = (Coding) ToolingExtensions.getExtension(c, ToolingExtensions.EXT_REPLACED_BY).getValue();
400          td.tx(" (replaced by ");
401          String url = getCodingReference(cc, system);
402          if (url != null) {
403            td.ah(url).addText(cc.getCode());
404            td.tx(": "+cc.getDisplay()+")");
405          } else
406            td.addText(cc.getCode()+" '"+cc.getDisplay()+"' in "+cc.getSystem()+")");
407        } else {
408          Extension ext = c.getExtensionByUrl(ToolingExtensions.EXT_STANDARDS_STATUS);
409          if (ext != null) {
410            ext = ext.getValue().getExtensionByUrl(ToolingExtensions.EXT_STANDARDS_STATUS_REASON);
411            if (ext != null) {
412              addMarkdown(td, ext.getValue().primitiveValue());
413            }
414          }
415        }
416      }
417    }
418    if (comment) {
419      td = tr.td();
420      Extension ext = c.getExtensionByUrl(ToolingExtensions.EXT_CS_COMMENT);
421      if (ext != null) {
422        hasExtensions = true;
423        String bc = ext.hasValue() ? ext.getValue().primitiveValue() : null;
424        Map<String, String> translations = ToolingExtensions.getLanguageTranslations(ext.getValue());
425
426        if (getContext().getLang() == null) {
427          if (bc != null)
428            td.addText(bc);
429        } else if (getContext().getLang().equals("*")) {
430          boolean sl = false;
431          for (String l : translations.keySet()) 
432            if (bc == null || !bc.equalsIgnoreCase(translations.get(l))) 
433              sl = true;
434          if (bc != null) {
435            td.addText((sl ? cs.getLanguage("en")+": " : "")+bc);
436          }
437          for (String l : translations.keySet()) {
438            if (bc == null || !bc.equalsIgnoreCase(translations.get(l))) {
439              if (!td.getChildNodes().isEmpty()) 
440                td.br();
441              td.addText(l+": "+translations.get(l));
442            }
443          }
444        } else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) {
445          if (bc != null)
446            td.addText(bc);
447        } else {
448          if (bc != null)
449            translations.put(cs.getLanguage("en"), bc);
450          for (String l : translations.keySet()) { 
451            if (l.equals(getContext().getLang())) {
452              td.addText(translations.get(l));
453            }
454          }
455        }
456      }      
457    }
458    if (version) {
459      td = tr.td();
460      if (c.hasUserData("cs.version.notes"))
461        td.addText(c.getUserString("cs.version.notes"));
462    }
463    if (properties != null) {
464      for (PropertyComponent pc : properties) {
465        td = tr.td();
466        boolean first = true;
467        List<ConceptPropertyComponent> pcvl = CodeSystemUtilities.getPropertyValues(c, pc.getCode());
468        for (ConceptPropertyComponent pcv : pcvl) {
469          if (pcv.hasValue()) {
470            if (first) first = false; else td.addText(", ");
471            if (pcv.hasValueCoding()) { 
472              td.addText(pcv.getValueCoding().getCode());
473            } else if (pcv.hasValueStringType() && Utilities.isAbsoluteUrlLinkable(pcv.getValue().primitiveValue())) {
474              td.ah(pcv.getValue().primitiveValue()).tx(pcv.getValue().primitiveValue());
475            } else {
476              td.addText(pcv.getValue().primitiveValue());
477            }
478          }
479        }
480      }
481    }
482    
483    for (UsedConceptMap m : maps) {
484      td = tr.td();
485      List<TargetElementComponentWrapper> mappings = findMappingsForCode(c.getCode(), m.getMap());
486      boolean first = true;
487      for (TargetElementComponentWrapper mapping : mappings) {
488        if (!first)
489            td.br();
490        first = false;
491        XhtmlNode span = td.span(null, mapping.comp.hasRelationship() ?  mapping.comp.getRelationship().toCode() : "");
492        span.addText(getCharForRelationship(mapping.comp));
493        a = td.ah(getContext().getLink(KnownLinkType.SPEC)+m.getLink()+"#"+makeAnchor(mapping.group.getTarget(), mapping.comp.getCode()));
494        a.addText(mapping.comp.getCode());
495        if (!Utilities.noString(mapping.comp.getComment()))
496          td.i().tx("("+mapping.comp.getComment()+")");
497      }
498    }
499    List<ConceptDefinitionComponent> ocl = csNav.getOtherChildren(c);
500    for (ConceptDefinitionComponent cc : csNav.getConcepts(c)) {
501      hasExtensions = addDefineRowToTable(t, cc, level+1, hasHierarchy, hasDisplay, hasDefinitions, comment, version, deprecated, maps, system, cs, properties, csNav, langs, isSupplement) || hasExtensions;
502    }
503    for (ConceptDefinitionComponent cc : ocl) {
504      tr = t.tr();
505      td = tr.td();
506      td.addText(Integer.toString(level+2));
507      td = tr.td();
508      String s = Utilities.padLeft("", '\u00A0', (level+1)*2);
509      td.addText(s);
510      td.attribute("style", "white-space:nowrap");
511      a = td.ah("#"+cs.getId()+"-" + Utilities.nmtokenize(cc.getCode()));
512      a.addText(cc.getCode());
513      if (hasDisplay) {
514        td = tr.td();
515        renderDisplayName(cc, cs, td);
516      }
517      int w = 1 + (deprecated ? 1 : 0) + (comment ? 1 : 0) + (version ? 1 : 0) + maps.size();
518      if (properties != null) {
519        w = w + properties.size();
520      }
521      td = tr.td().colspan(Integer.toString(w));
522    }
523    if (context.isCopyButton()) {
524      td = tr.td();
525      clipboard(td, "icon_clipboard_x.png", "XML", "<system value=\""+Utilities.escapeXml(cs.getUrl())+"\">\n"+(cs.getVersionNeeded() ? "<version value=\""+Utilities.escapeXml(cs.getVersion())+"\">\n" : "")+"<code value=\""+Utilities.escapeXml(c.getCode())+"\">\n<display value=\""+Utilities.escapeXml(c.getDisplay())+"\">\n");
526      td.nbsp();
527      clipboard(td, "icon_clipboard_j.png", "JSON", "\"system\" : \""+Utilities.escapeXml(cs.getUrl())+"\",\n"+(cs.getVersionNeeded() ? "\"version\" : \""+Utilities.escapeXml(cs.getVersion())+"\",\n" : "")+"\"code\" : \""+Utilities.escapeXml(c.getCode())+"\",\n\"display\" : \""+Utilities.escapeXml(c.getDisplay())+"\"\n");
528    }
529    return hasExtensions;
530  }
531
532  private boolean hasMarkdownInDefinitions(CodeSystem cs) {
533    return ToolingExtensions.readBoolExtension(cs, "http://hl7.org/fhir/StructureDefinition/codesystem-use-markdown");
534  }
535
536
537  public void renderDisplayName(ConceptDefinitionComponent c, CodeSystem cs, XhtmlNode td) {
538    if (c.hasDisplayElement()) {
539      if (getContext().getLang() == null) {
540        td.addText(c.getDisplay());
541      } else if (getContext().getLang().equals("*")) {
542        boolean sl = false;
543        for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) 
544          if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "display") && cd.hasLanguage() && !c.getDisplay().equalsIgnoreCase(cd.getValue())) 
545            sl = true;
546        td.addText((sl ? cs.getLanguage("en")+": " : "")+c.getDisplay());
547        for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
548          if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "display") && cd.hasLanguage() && !c.getDisplay().equalsIgnoreCase(cd.getValue())) {
549            td.br();
550            td.addText(cd.getLanguage()+": "+cd.getValue());
551          }
552        }
553     } else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) {
554       td.addText(c.getDisplay());
555     } else {
556       for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
557         if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "display") && cd.hasLanguage() && cd.getLanguage().equals(getContext().getLang())) {
558           td.addText(cd.getValue());
559         }
560       }
561     }
562    }
563  }
564
565  private String getCodingReference(Coding cc, String system) {
566    if (cc.getSystem().equals(system))
567      return "#"+cc.getCode();
568    if (cc.getSystem().equals("http://snomed.info/sct"))
569      return "http://snomed.info/sct/"+cc.getCode();
570    if (cc.getSystem().equals("http://loinc.org"))
571      return LoincLinker.getLinkForCode(cc.getCode());
572    return null;
573  }
574
575
576  private void addLanguageRow(ConceptDefinitionComponent c, XhtmlNode t, List<String> langs) {
577    XhtmlNode tr = t.tr();
578    tr.td().addText(c.getCode());
579    for (String lang : langs) {
580      ConceptDefinitionDesignationComponent d = null;
581      for (ConceptDefinitionDesignationComponent designation : c.getDesignation()) {
582        if (designation.hasLanguage()) {
583          if (lang.equals(designation.getLanguage()))
584            d = designation;
585        }
586      }
587      tr.td().addText(d == null ? "" : d.getValue());
588    }
589  }
590 
591}