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