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