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