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