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