001package org.hl7.fhir.r5.renderers.utils;
002
003import java.io.IOException;
004import java.text.NumberFormat;
005import java.time.ZoneId;
006import java.time.format.DateTimeFormatter;
007import java.util.ArrayList;
008import java.util.HashMap;
009import java.util.HashSet;
010import java.util.List;
011import java.util.Locale;
012import java.util.Map;
013import java.util.Set;
014
015import org.hl7.fhir.exceptions.FHIRException;
016import org.hl7.fhir.exceptions.FHIRFormatError;
017import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider;
018import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
019import org.hl7.fhir.r5.context.IWorkerContext;
020import org.hl7.fhir.r5.elementmodel.Element;
021import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IEvaluationContext;
022import org.hl7.fhir.r5.model.Base;
023import org.hl7.fhir.r5.model.DomainResource;
024import org.hl7.fhir.r5.model.Enumeration;
025import org.hl7.fhir.r5.model.PrimitiveType;
026import org.hl7.fhir.r5.model.StringType;
027import org.hl7.fhir.r5.renderers.utils.Resolver.IReferenceResolver;
028import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
029import org.hl7.fhir.r5.utils.ToolingExtensions;
030import org.hl7.fhir.utilities.FhirPublication;
031import org.hl7.fhir.utilities.MarkDownProcessor;
032import org.hl7.fhir.utilities.MarkDownProcessor.Dialect;
033import org.hl7.fhir.utilities.StandardsStatus;
034import org.hl7.fhir.utilities.Utilities;
035import org.hl7.fhir.utilities.i18n.RenderingI18nContext;
036import org.hl7.fhir.utilities.validation.ValidationOptions;
037
038/**
039 * Managing Language when rendering 
040 * 
041 * You can specify a language to use when rendering resources by setting the setLocale() on 
042 * the super class. The locale drives the following:
043 * - choice of java supplied rendering phrase, if translations are provided for the locale 
044 * - integer and date formats used (but see below for date formats)
045 * - automatic translation of coded values, if language supplements are available
046 * - choosing text representation considering the FHIR translation extension
047 *    
048 * By default, the locale is null, and the default locale for the underlying system is used. 
049 * If you set locale to a specific value, then that value will be used instead of the default locale.
050 *    
051 * By default, only a single language is rendered, based on the locale. Where resources contain
052 * multiple language content (designations in CodeSystem and ValueSet, or using the translation
053 * extension), you can control what languages are presented using the properties multiLanguagePolicy
054 * and languages
055 * - multiLanguagePolicy: NONE (default), DESIGNATIONS, ALL
056 * - languages: a list of allowed languages. Default is empty which means all languages in scope via multiLanguagePolicy
057 * 
058 * Managing Date/Time Formatting
059 * 
060 * This class has multiple parameters that influence date/time formatting when rendering resources 
061 * 
062 * - The default rendering is using the default java locale as above
063 * - If you setLocale() to something, then the defaults for the locale will be used 
064 * - Else you can set the values of dateTimeFormat, dateFormat, dateYearFormat and dateYearMonthFormat
065 * 
066 * If you set the value of locale, the values of dateTimeFormat, dateFormat, dateYearFormat and dateYearMonthFormat are 
067 * reset to the system defaults 
068 * 
069 * Timezones: by default, date/times are rendered in their source timezone 
070 * 
071 */
072public class RenderingContext extends RenderingI18nContext {
073
074  // provides liquid templates, if they are available for the content
075  public interface ILiquidTemplateProvider {
076    String findTemplate(RenderingContext rcontext, DomainResource r);
077    String findTemplate(RenderingContext rcontext, String resourceName);
078  }
079
080  // parses xml to an XML instance. Whatever codes provides this needs to provide something that parses the right version 
081  public interface ITypeParser {
082    Base parseType(String xml, String type) throws FHIRFormatError, IOException, FHIRException ;
083    Base parseType(Element base) throws FHIRFormatError, IOException, FHIRException ;
084  }
085
086  /**
087   * What kind of user the renderer is targeting - end users, or technical users
088   * 
089   * This affects the way codes and references are rendered
090   * 
091   * @author graha
092   *
093   */
094  public enum ResourceRendererMode {
095    /**
096     * The user has no interest in the contents of the FHIR resource, and just wants to see the data
097     * 
098     */
099    END_USER,
100    
101    /**
102     * The user wants to see the resource, but a technical view so they can see what's going on with the content
103     */
104    TECHNICAL
105  }
106
107  public enum GenerationRules {
108    /**
109     * The output must be valid XHTML for a resource: no active content, etc. The only external dependency allowed is fhir.css 
110     */
111    VALID_RESOURCE,
112    
113    /**
114     * The output must be valid for an implementation guide according ot the base FHIR template. 
115     * This means active content is allowed, though the default presentation must be *show everything* for balloting purposes
116     * Active content is allowed 
117     */
118    IG_PUBLISHER
119  }
120  
121  public enum StructureDefinitionRendererMode {
122    SUMMARY, // 5 cells: tree/name | flags | cardinality | type | details
123    BINDINGS, // tree/name + column for each kind of binding found, cells are lists of bindings 
124    OBLIGATIONS, // tree/name + column for each actor that has obligations
125    DATA_DICT,  // detailed element view 
126  }
127
128  public enum ExampleScenarioRendererMode {
129    /**
130     * A visual presentation of the questionnaire, with a set of property panes that can be toggled on and off.
131     * Note that this is not the same as how the questionnaire would like on a form filler, since all dynamic behavior is ignored
132     */
133    ACTORS,
134
135    /**
136     * A table listing all the instances (and versions there-of) used in a scenario
137     */
138    INSTANCES,
139
140    /**
141     * Information about the processes (and sub-processes) defined in a scenario
142     */
143    PROCESSES,
144
145    /**
146     * A diagram showing interactions between the actors as part of the process steps
147     */
148    DIAGRAM
149  }
150
151  public enum QuestionnaireRendererMode {
152    /**
153     * A visual presentation of the questionnaire, with a set of property panes that can be toggled on and off.
154     * Note that this is not the same as how the questionnaire would like on a form filler, since all dynamic behavior is ignored
155     */
156    FORM,
157
158    /**
159     * a structured tree that presents the content of the questionnaire in a logical fashion
160     */
161    TREE,   
162
163    /**
164     * A structured tree that presents the enableWhen, terminology and expression bindings for the questionnaire 
165     */
166    LOGIC,
167
168    /**
169     * A presentation that lists all the items, with full details about them 
170     */
171    DEFNS, 
172
173    /**
174     * Rendered links to various openly available Form Filler applications that know how to render a questionnaire published in a package 
175     */
176    LINKS
177  }
178
179
180  public enum KnownLinkType {
181    SELF,  // absolute link to where the content is to be found (only used in a few circumstances when making external references to tools)
182    SPEC,  // version specific link to core specification
183    JSON_NAMES
184    
185  }
186  
187  public enum FixedValueFormat {
188    JSON, JSON_ALL, XML, XML_ALL;
189
190    public static FixedValueFormat fromCode(String value) {
191      if (value == null) {
192        return JSON;
193      }
194      switch (value.toLowerCase()) {
195      case "json" : return JSON;
196      case "json-all" : return JSON_ALL;
197      case "xml" : return XML;
198      case "xml-all" : return XML_ALL;
199      }
200      return JSON;
201    }
202
203    public boolean notPrimitives() {
204      return this == JSON || this == XML;
205    }
206
207    public boolean isXml() {
208      return this == XML_ALL || this == XML;
209    }
210  }
211
212  public enum MultiLanguagePolicy {
213    NONE, // ONLY render the language in the locale
214    DESIGNATIONS,  // in addition to the locale language, render designations from other languages (eg. as found in code systems and value sets
215    ALL // in addition to translations in designations, look for an render translations (WIP)
216  }
217
218  private IWorkerContext worker;
219  private MarkDownProcessor markdown;
220  private ResourceRendererMode mode;
221  private GenerationRules rules;
222  private IReferenceResolver resolver;
223  private ILiquidTemplateProvider templateProvider;
224  private IEvaluationContext services;
225  private ITypeParser parser;
226
227  // i18n related fields
228  private MultiLanguagePolicy multiLanguagePolicy = MultiLanguagePolicy.NONE;
229  private Set<String> allowedLanguages = new HashSet<>(); 
230  private ZoneId timeZoneId;
231  private DateTimeFormatter dateTimeFormat;
232  private DateTimeFormatter dateFormat;
233  private DateTimeFormatter dateYearFormat;
234  private DateTimeFormatter dateYearMonthFormat;
235  
236  private String localPrefix; // relative link within local context
237  private int headerLevelContext;
238  private boolean canonicalUrlsAsLinks;
239  private boolean pretty;
240  private boolean header;
241  private boolean contained;
242
243  private ValidationOptions terminologyServiceOptions = new ValidationOptions(FhirPublication.R5);
244  private boolean noSlowLookup;
245  private List<String> codeSystemPropList = new ArrayList<>();
246
247  private ProfileUtilities profileUtilitiesR;
248  private String definitionsTarget;
249  private String destDir;
250  private boolean inlineGraphics;
251  private StandardsStatus defaultStandardsStatus;
252
253  private ExampleScenarioRendererMode scenarioMode = null;
254  private QuestionnaireRendererMode questionnaireMode = QuestionnaireRendererMode.FORM;
255  private StructureDefinitionRendererMode structureMode = StructureDefinitionRendererMode.SUMMARY;
256  private FixedValueFormat fixedFormat = FixedValueFormat.JSON;
257  
258  private boolean addGeneratedNarrativeHeader = true;
259  private boolean showComments = false;
260
261  private FhirPublication targetVersion;
262  private boolean copyButton;
263  private ProfileKnowledgeProvider pkp;
264  private String changeVersion;
265  private List<String> files = new ArrayList<String>(); // files created as by-products in destDir
266  
267  private Map<KnownLinkType, String> links = new HashMap<>();
268  private Map<String, String> namedLinks = new HashMap<>();
269  private boolean addName = false;
270  private Map<String, String> typeMap = new HashMap<>(); // type aliases that can be resolved in Markdown type links (mainly for cross-version usage)
271  
272  /**
273   * 
274   * @param context - access to all related resources that might be needed
275   * @param markdown - appropriate markdown processing engine 
276   * @param terminologyServiceOptions - options to use when looking up codes
277   * @param specLink - path to FHIR specification
278   * @param locale - i18n for rendering
279   */
280  public RenderingContext(IWorkerContext worker, MarkDownProcessor markdown, ValidationOptions terminologyServiceOptions, String specLink, String localPrefix, Locale locale, ResourceRendererMode mode, GenerationRules rules) {
281    super();
282    this.worker = worker;
283    this.markdown = markdown;
284    this.locale = locale;
285    this.links.put(KnownLinkType.SPEC, specLink);
286    this.localPrefix = localPrefix;
287    this.mode = mode;
288    this.rules = rules;
289    if (terminologyServiceOptions != null) {
290      this.terminologyServiceOptions = terminologyServiceOptions;
291    }
292  }
293  
294  public RenderingContext copy() {
295    RenderingContext res = new RenderingContext(worker, markdown, terminologyServiceOptions, getLink(KnownLinkType.SPEC), localPrefix, locale, mode, rules);
296
297    res.resolver = resolver;
298    res.templateProvider = templateProvider;
299    res.services = services;
300    res.parser = parser;
301
302    res.headerLevelContext = headerLevelContext;
303    res.canonicalUrlsAsLinks = canonicalUrlsAsLinks;
304    res.pretty = pretty;
305    res.contained = contained;
306    
307    res.noSlowLookup = noSlowLookup;
308    res.codeSystemPropList.addAll(codeSystemPropList);
309
310    res.profileUtilitiesR = profileUtilitiesR;
311    res.definitionsTarget = definitionsTarget;
312    res.destDir = destDir;
313    res.addGeneratedNarrativeHeader = addGeneratedNarrativeHeader;
314    res.scenarioMode = scenarioMode;
315    res.questionnaireMode = questionnaireMode;
316    res.structureMode = structureMode;
317    res.header = header;
318    res.links.putAll(links);
319    res.inlineGraphics = inlineGraphics;
320    res.timeZoneId = timeZoneId;
321    res.dateTimeFormat = dateTimeFormat;
322    res.dateFormat = dateFormat;
323    res.dateYearFormat = dateYearFormat;
324    res.dateYearMonthFormat = dateYearMonthFormat;
325    res.targetVersion = targetVersion;
326    res.showComments = showComments;
327    res.copyButton = copyButton;
328    res.pkp = pkp;
329    res.defaultStandardsStatus = defaultStandardsStatus;
330    res.changeVersion = changeVersion;
331
332    res.terminologyServiceOptions = terminologyServiceOptions.copy();
333    res.typeMap.putAll(typeMap);
334    res.multiLanguagePolicy = multiLanguagePolicy;
335    res.allowedLanguages.addAll(allowedLanguages);
336    return res;
337  }
338  
339
340  public IWorkerContext getContext() {
341    return worker;
342  }
343
344
345  
346  // -- 2. Markdown support -------------------------------------------------------
347
348  public ProfileUtilities getProfileUtilities() {
349    if (profileUtilitiesR == null) {
350      profileUtilitiesR = new ProfileUtilities(worker, null, pkp);
351    }
352    return profileUtilitiesR;
353  }
354
355  public IWorkerContext getWorker() {
356    return worker;
357  }
358
359  public boolean isCanonicalUrlsAsLinks() {
360    return canonicalUrlsAsLinks;
361  }
362
363  public RenderingContext setCanonicalUrlsAsLinks(boolean canonicalUrlsAsLinks) {
364    this.canonicalUrlsAsLinks = canonicalUrlsAsLinks;
365    return this;
366  }
367
368  public MarkDownProcessor getMarkdown() {
369    if (markdown == null) {
370      markdown = new MarkDownProcessor(Dialect.COMMON_MARK);
371    }
372    return markdown;
373  }
374
375  public MultiLanguagePolicy getMultiLanguagePolicy() {
376    return multiLanguagePolicy;
377  }
378
379  public void setMultiLanguagePolicy(MultiLanguagePolicy multiLanguagePolicy) {
380    this.multiLanguagePolicy = multiLanguagePolicy;
381  }
382
383  public Set<String> getAllowedLanguages() {
384    return allowedLanguages;
385  }
386
387  public String getLocalPrefix() {
388    return localPrefix;
389  }
390
391  public ValidationOptions getTerminologyServiceOptions() {
392    return terminologyServiceOptions;
393  }
394
395  public int getHeaderLevelContext() {
396    return headerLevelContext;
397  }
398
399  public RenderingContext setHeaderLevelContext(int headerLevelContext) {
400    this.headerLevelContext = headerLevelContext;
401    return this;
402  }
403
404  public IReferenceResolver getResolver() {
405    return resolver;
406  }
407
408  public RenderingContext setResolver(IReferenceResolver resolver) {
409    this.resolver = resolver;
410    return this;
411  }
412
413  public RenderingContext setTerminologyServiceOptions(ValidationOptions terminologyServiceOptions) {
414    this.terminologyServiceOptions = terminologyServiceOptions;
415    return this;
416  }
417
418  public boolean isNoSlowLookup() {
419    return noSlowLookup;
420  }
421
422  public RenderingContext setNoSlowLookup(boolean noSlowLookup) {
423    this.noSlowLookup = noSlowLookup;
424    return this;
425  }
426
427  public String getDefinitionsTarget() {
428    return definitionsTarget;
429  }
430
431  public RenderingContext setDefinitionsTarget(String definitionsTarget) {
432    this.definitionsTarget = definitionsTarget;
433    return this;
434  }
435
436  public String getDestDir() {
437    return destDir;
438  }
439
440  public RenderingContext setDestDir(String destDir) {
441    this.destDir = destDir;
442    return this;
443  }
444
445  public RenderingContext setProfileUtilities(ProfileUtilities profileUtilities) {
446    this.profileUtilitiesR = profileUtilities;
447    if (pkp == null && profileUtilities.getPkp() != null) {
448      pkp = profileUtilities.getPkp();
449    }
450    return this;
451  }
452
453  public ILiquidTemplateProvider getTemplateProvider() {
454    return templateProvider;
455  }
456
457  public RenderingContext setTemplateProvider(ILiquidTemplateProvider templateProvider) {
458    this.templateProvider = templateProvider;
459    return this;
460  }
461
462  public IEvaluationContext getServices() {
463    return services;
464  }
465
466  public RenderingContext setServices(IEvaluationContext services) {
467    this.services = services;
468    return this;
469  }
470
471  public boolean isPretty() {
472    return pretty;
473  }
474
475  public RenderingContext setPretty(boolean pretty) {
476    this.pretty = pretty;
477    return this;
478  }
479
480  public ITypeParser getParser() {
481    return parser;
482  }
483
484  public RenderingContext setParser(ITypeParser parser) {
485    this.parser = parser;
486    return this;
487  }
488
489
490  public List<String> getCodeSystemPropList() {
491    return codeSystemPropList;
492  }
493
494  public RenderingContext setCodeSystemPropList(List<String> codeSystemPropList) {
495    this.codeSystemPropList = codeSystemPropList;
496    return this;
497  }
498
499
500  public boolean isInlineGraphics() {
501    return inlineGraphics;
502  }
503
504  public RenderingContext setInlineGraphics(boolean inlineGraphics) {
505    this.inlineGraphics = inlineGraphics;
506    return this;
507  }
508
509  public boolean isHeader() {
510    return header;
511  }
512
513  public RenderingContext setHeader(boolean header) {
514    this.header = header;
515    return this;
516  }
517
518  public ExampleScenarioRendererMode getScenarioMode() {
519    return scenarioMode;
520  }
521
522  public RenderingContext setScenarioMode(ExampleScenarioRendererMode scenarioMode) {
523    this.scenarioMode = scenarioMode;
524    return this;
525  }
526
527  public QuestionnaireRendererMode getQuestionnaireMode() {
528    return questionnaireMode;
529  }
530
531  public RenderingContext setQuestionnaireMode(QuestionnaireRendererMode questionnaireMode) {
532    this.questionnaireMode = questionnaireMode;
533    return this;
534  }
535  
536  public StructureDefinitionRendererMode getStructureMode() {
537    return structureMode;
538  }
539
540  public RenderingContext setStructureMode(StructureDefinitionRendererMode structureMode) {
541    this.structureMode = structureMode;
542    return this;
543  }
544
545  public String fixReference(String ref) {
546    if (!Utilities.isAbsoluteUrl(ref)) {
547      return (localPrefix == null ? "" : localPrefix)+ref;
548    }
549    if (ref.startsWith("http://hl7.org/fhir") && !ref.substring(20).contains("/")) {
550      return getLink(KnownLinkType.SPEC)+ref.substring(20);
551    }
552    return ref;
553  }
554
555  public RenderingContext setLocalPrefix(String localPrefix) {
556    this.localPrefix = localPrefix;
557    return this;
558  }
559
560  public boolean isAddGeneratedNarrativeHeader() {
561    return addGeneratedNarrativeHeader;
562  }
563
564  public RenderingContext setAddGeneratedNarrativeHeader(boolean addGeneratedNarrativeHeader) {
565    this.addGeneratedNarrativeHeader = addGeneratedNarrativeHeader;
566    return this;
567   }
568
569  public FhirPublication getTargetVersion() {
570    return targetVersion;
571  }
572
573  public RenderingContext setTargetVersion(FhirPublication targetVersion) {
574    this.targetVersion = targetVersion;
575    return this;
576  }
577
578  public boolean isTechnicalMode() {
579    return mode == ResourceRendererMode.TECHNICAL;
580  }
581
582  /**
583   * if the timezone is null, the rendering will default to the source timezone
584   * in the resource
585   * 
586   * Note that if you're working server side, the FHIR project recommends the use
587   * of the Date header so that clients know what timezone the server defaults to,
588   * 
589   * There is no standard way for the server to know what the client timezone is. 
590   * In the case where the client timezone is unknown, the timezone should be null
591   *
592   * @return the specified timezone to render in
593   */
594  public ZoneId getTimeZoneId() {
595    return timeZoneId;
596  }
597
598  public RenderingContext setTimeZoneId(ZoneId timeZoneId) {
599    this.timeZoneId = timeZoneId;
600    return this;
601  }
602
603
604  /**
605   * In the absence of a specified format, the renderers will default to 
606   * the FormatStyle.MEDIUM for the current locale.
607   * 
608   * @return the format to use
609   */
610  public DateTimeFormatter getDateTimeFormat() {
611    return this.dateTimeFormat;
612  }
613
614  public RenderingContext setDateTimeFormat(DateTimeFormatter dateTimeFormat) {
615    this.dateTimeFormat = dateTimeFormat;
616    return this;
617  }
618
619  public RenderingContext setDateTimeFormatString(String dateTimeFormat) {
620    this.dateTimeFormat = DateTimeFormatter.ofPattern(dateTimeFormat);
621    return this;
622  }
623
624  /**
625   * In the absence of a specified format, the renderers will default to 
626   * the FormatStyle.MEDIUM for the current locale.
627   * 
628   * @return the format to use
629   */
630  public DateTimeFormatter getDateFormat() {
631    return this.dateFormat;
632  }
633
634  public RenderingContext setDateFormat(DateTimeFormatter dateFormat) {
635    this.dateFormat = dateFormat;
636    return this;
637  }
638
639  public RenderingContext setDateFormatString(String dateFormat) {
640    this.dateFormat = DateTimeFormatter.ofPattern(dateFormat);
641    return this;
642  }
643
644  public DateTimeFormatter getDateYearFormat() {
645    return dateYearFormat;
646  }
647
648  public RenderingContext setDateYearFormat(DateTimeFormatter dateYearFormat) {
649    this.dateYearFormat = dateYearFormat;
650    return this;
651  }
652
653  public RenderingContext setDateYearFormatString(String dateYearFormat) {
654    this.dateYearFormat = DateTimeFormatter.ofPattern(dateYearFormat);
655    return this;
656  }
657
658  public DateTimeFormatter getDateYearMonthFormat() {
659    return dateYearMonthFormat;
660  }
661
662  public RenderingContext setDateYearMonthFormat(DateTimeFormatter dateYearMonthFormat) {
663    this.dateYearMonthFormat = dateYearMonthFormat;
664    return this;
665  }
666
667  public RenderingContext setDateYearMonthFormatString(String dateYearMonthFormat) {
668    this.dateYearMonthFormat = DateTimeFormatter.ofPattern(dateYearMonthFormat);
669    return this;
670  }
671
672  public ResourceRendererMode getMode() {
673    return mode;
674  }
675
676  public RenderingContext setMode(ResourceRendererMode mode) {
677    this.mode = mode;
678    return this;
679  }
680
681  public boolean isContained() {
682    return contained;
683  }
684
685  public RenderingContext setContained(boolean contained) {
686    this.contained = contained;
687    return this;
688  }
689  public boolean isShowComments() {
690    return showComments;
691  }
692  public RenderingContext setShowComments(boolean showComments) {
693    this.showComments = showComments;
694    return this;
695  }
696  public boolean isCopyButton() {
697    return copyButton;
698  }
699  public RenderingContext setCopyButton(boolean copyButton) {
700    this.copyButton = copyButton;
701    return this;
702  }
703  
704  public RenderingContext setPkp(ProfileKnowledgeProvider pkp) {
705    this.pkp = pkp;
706    return this;
707  }
708  public ProfileKnowledgeProvider getPkp() {
709    return pkp;
710  }
711  
712  public boolean hasLink(KnownLinkType link) {
713    return links.containsKey(link);
714  }
715  
716  public String getLink(KnownLinkType link) {
717    return links.get(link);
718  }
719  public void addLink(KnownLinkType type, String link) {
720    links.put(type, link);
721    
722  }
723  public GenerationRules getRules() {
724    return rules;
725  }
726  public RenderingContext setRules(GenerationRules rules) {
727    this.rules = rules;
728    return this;
729  }
730  public StandardsStatus getDefaultStandardsStatus() {
731    return defaultStandardsStatus;
732  }
733  public RenderingContext setDefaultStandardsStatus(StandardsStatus defaultStandardsStatus) {
734    this.defaultStandardsStatus = defaultStandardsStatus;
735    return this;
736  }
737
738  public String getChangeVersion() {
739    return changeVersion;
740  }
741
742  public RenderingContext setChangeVersion(String changeVersion) {
743    this.changeVersion = changeVersion;
744    return this;
745  }
746
747  public Map<String, String> getNamedLinks() {
748    return namedLinks;
749  }
750
751  public void registerFile(String n) {
752    try {
753      files.add(Utilities.path(destDir, n));
754    } catch (IOException e) {
755    }
756  }
757
758  public List<String> getFiles() {
759    return files;
760  }
761
762  public FixedValueFormat getFixedFormat() {
763    return fixedFormat;
764  }
765
766  public void setFixedFormat(FixedValueFormat fixedFormat) {
767    this.fixedFormat = fixedFormat;
768  }
769
770  public boolean isAddName() {
771    return addName;
772  }
773
774  public RenderingContext setAddName(boolean addName) {
775    this.addName = addName;
776    return this;
777  }
778
779  public Map<String, String> getTypeMap() {
780    return typeMap;
781  }
782
783
784  public String toStr(int v) {
785    NumberFormat nf = NumberFormat.getInstance(locale);
786    return nf.format(v);
787  }
788
789
790  public String getTranslated(PrimitiveType<?> t) {
791    if (locale != null) {
792      String v = ToolingExtensions.getLanguageTranslation(t, locale.toString());
793      if (v != null) {
794        return v;
795      }
796    }
797    return t.asStringValue();
798  }
799
800  public StringType getTranslatedElement(PrimitiveType<?> t) {
801    if (locale != null) {
802      StringType v = ToolingExtensions.getLanguageTranslationElement(t, locale.toString());
803      if (v != null) {
804        return v;
805      }
806    }
807    if (t instanceof StringType) {
808      return (StringType) t;
809    } else {
810      return new StringType(t.asStringValue());
811    }
812  }
813
814  public String getTranslatedCode(Base b, String codeSystem) {
815
816    if (b instanceof org.hl7.fhir.r5.model.Element) {
817      org.hl7.fhir.r5.model.Element e = (org.hl7.fhir.r5.model.Element) b;
818      if (locale != null) {
819        String v = ToolingExtensions.getLanguageTranslation(e, locale.toString());
820        if (v != null) {
821          return v;
822        }
823        // no? then see if the tx service can translate it for us 
824        try {
825          ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(locale.toString()).withVersionFlexible(true),
826              codeSystem, null, e.primitiveValue(), null);
827          if (t.isOk() && t.getDisplay() != null) {
828            return t.getDisplay();
829          }
830        } catch (Exception ex) {
831          // nothing
832        }
833      }
834      if (e instanceof Enumeration<?>) {
835        return ((Enumeration<?>) e).getDisplay();
836      } else {
837        return e.primitiveValue();
838      }
839    } else if (b instanceof Element) {
840      return getTranslatedCode((Element) b, codeSystem);
841    } else {
842      return "??";
843    }
844  }
845
846  public String getTranslatedCode(Enumeration<?> e, String codeSystem) {
847    if (locale != null) {
848      String v = ToolingExtensions.getLanguageTranslation(e, locale.toString());
849      if (v != null) {
850        return v;
851      }
852      // no? then see if the tx service can translate it for us 
853      try {
854        ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(locale.toString()).withVersionFlexible(true),
855            codeSystem, null, e.getCode(), null);
856        if (t.isOk() && t.getDisplay() != null) {
857          return t.getDisplay();
858        }
859      } catch (Exception ex) {
860        // nothing
861      }
862    }
863    try {
864      ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withVersionFlexible(true),
865          codeSystem, null, e.getCode(), null);
866      if (t.isOk() && t.getDisplay() != null) {
867        return t.getDisplay();
868      }
869    } catch (Exception ex) {
870      // nothing
871    }
872    
873    return e.getCode();
874  }
875  
876  public String getTranslatedCode(Element e, String codeSystem) {
877    if (locale != null) {
878      // first we look through the translation extensions
879      for (Element ext : e.getChildrenByName("extension")) {
880        String url = ext.getNamedChildValue("url");
881        if (url.equals(ToolingExtensions.EXT_TRANSLATION)) {
882          Base e1 = ext.getExtensionValue("lang");
883
884          if (e1 != null && e1.primitiveValue() != null && e1.primitiveValue().equals(locale.toString())) {
885            e1 = ext.getExtensionValue("content");
886            if (e1 != null && e1.isPrimitive()) {
887              return e1.primitiveValue();
888            }
889          }
890        }
891      }
892      // no? then see if the tx service can translate it for us 
893      try {
894        ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(locale.toString()).withVersionFlexible(true),
895            codeSystem, null, e.primitiveValue(), null);
896        if (t.isOk() && t.getDisplay() != null) {
897          return t.getDisplay();
898        }
899      } catch (Exception ex) {
900        // nothing
901      }
902    }
903    return e.primitiveValue();
904  }
905
906  public RenderingContext withLocale(Locale locale) {
907    setLocale(locale);
908    return this;
909  }
910
911  public RenderingContext withLocaleCode(String locale) {
912    setLocale(new Locale(locale));
913    return this;
914  }
915  
916}