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