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