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