
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.*; 008 009import lombok.Getter; 010import lombok.Setter; 011import org.hl7.fhir.exceptions.FHIRException; 012import org.hl7.fhir.exceptions.FHIRFormatError; 013import org.hl7.fhir.r5.conformance.profile.ProfileKnowledgeProvider; 014import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; 015import org.hl7.fhir.r5.context.ContextUtilities; 016import org.hl7.fhir.r5.context.IWorkerContext; 017import org.hl7.fhir.r5.elementmodel.Element; 018import org.hl7.fhir.r5.extensions.ExtensionDefinitions; 019import org.hl7.fhir.r5.extensions.ExtensionUtilities; 020import org.hl7.fhir.r5.fhirpath.IHostApplicationServices; 021import org.hl7.fhir.r5.model.ActorDefinition; 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.PackageInformation; 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; 031 032import org.hl7.fhir.utilities.*; 033import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; 034import org.hl7.fhir.utilities.i18n.RenderingI18nContext; 035import org.hl7.fhir.utilities.validation.ValidationOptions; 036 037/** 038 * Managing Language when rendering 039 * 040 * You can specify a language to use when rendering resources by setting the setLocale() on 041 * the super class. The locale drives the following: 042 * - choice of java supplied rendering phrase, if translations are provided for the locale 043 * - integer and date formats used (but see below for date formats) 044 * - automatic translation of coded values, if language supplements are available 045 * - choosing text representation considering the FHIR translation extension 046 * 047 * By default, the locale is null, and the default locale for the underlying system is used. 048 * If you set locale to a specific value, then that value will be used instead of the default locale. 049 * 050 * By default, only a single language is rendered, based on the locale. Where resources contain 051 * multiple language content (designations in CodeSystem and ValueSet, or using the translation 052 * extension), you can control what languages are presented using the properties multiLanguagePolicy 053 * and languages 054 * - multiLanguagePolicy: NONE (default), DESIGNATIONS, ALL 055 * - languages: a list of allowed languages. Default is empty which means all languages in scope via multiLanguagePolicy 056 * 057 * Managing Date/Time Formatting 058 * 059 * This class has multiple parameters that influence date/time formatting when rendering resources 060 * 061 * - The default rendering is using the default java locale as above 062 * - If you setLocale() to something, then the defaults for the locale will be used 063 * - Else you can set the values of dateTimeFormat, dateFormat, dateYearFormat and dateYearMonthFormat 064 * 065 * If you set the value of locale, the values of dateTimeFormat, dateFormat, dateYearFormat and dateYearMonthFormat are 066 * reset to the system defaults 067 * 068 * Timezones: by default, date/times are rendered in their source timezone 069 * 070 */ 071@MarkedToMoveToAdjunctPackage 072public class RenderingContext extends RenderingI18nContext { 073 074 075 public enum DesignationMode { 076 ALL, 077 LANGUAGES, 078 NONE 079 } 080 081 public interface IResourceLinkResolver { 082 public <T extends Resource> T findLinkableResource(Class<T> class_, String uri) throws IOException; 083 } 084 085 public static class RenderingContextLangs { 086 087 private final RenderingContext defLangRC; 088 private final Map<String, RenderingContext> langs = new HashMap<>(); 089 090 public RenderingContextLangs(RenderingContext defLangRC) { 091 this.defLangRC = defLangRC; 092 } 093 094 public void seeLang(String lang, RenderingContext rc) { 095 this.langs.put(lang, rc); 096 } 097 098 public RenderingContext get(String lang) { 099 if (lang == null || !langs.containsKey(lang)) { 100 return defLangRC; 101 } else { 102 return langs.get(lang); 103 } 104 } 105 106 public void setNoHeader(boolean b) { 107 defLangRC.setNoHeader(b); 108 for (RenderingContext rc : langs.values()) { 109 rc.setNoHeader(b); 110 } 111 } 112 113 public Collection<RenderingContext> langValues() { 114 return langs.values(); 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 267 268 269 270 271 272 273 274 275 276 277 278 private final IWorkerContext worker; 279 private MarkDownProcessor markdown; 280 private ResourceRendererMode mode; 281 private GenerationRules rules; 282 private IReferenceResolver resolver; 283 private ILiquidTemplateProvider templateProvider; 284 private IHostApplicationServices services; 285 private ITypeParser parser; 286 287 // i18n related fields 288 private boolean secondaryLang; // true if this is not the primary language for the resource 289 private MultiLanguagePolicy multiLanguagePolicy = MultiLanguagePolicy.NONE; 290 private Set<String> allowedLanguages = new HashSet<>(); 291 private ZoneId timeZoneId; 292 private DateTimeFormatter dateTimeFormat; 293 private DateTimeFormatter dateFormat; 294 private DateTimeFormatter dateYearFormat; 295 private DateTimeFormatter dateYearMonthFormat; 296 297 private String localPrefix; // relative link within local context 298 private int headerLevelContext; 299 private boolean canonicalUrlsAsLinks; 300 private boolean pretty; 301 private boolean showSummaryTable; // for canonical resources 302 private boolean contained; 303 private boolean oids; 304 305 306 private ValidationOptions terminologyServiceOptions = new ValidationOptions(FhirPublication.R5); 307 private boolean noSlowLookup; 308 private List<String> codeSystemPropList = new ArrayList<>(); 309 310 private ProfileUtilities profileUtilitiesR; 311 private ContextUtilities contextUtilities; 312 private String definitionsTarget; 313 private String destDir; 314 private boolean inlineGraphics; 315 private StandardsStatus defaultStandardsStatus; 316 317 private ExampleScenarioRendererMode scenarioMode = null; 318 private QuestionnaireRendererMode questionnaireMode = QuestionnaireRendererMode.FORM; 319 private StructureDefinitionRendererMode structureMode = StructureDefinitionRendererMode.SUMMARY; 320 private FixedValueFormat fixedFormat = FixedValueFormat.JSON; 321 322 private boolean showComments = false; 323 324 private FhirPublication targetVersion; 325 private boolean copyButton; 326 private ProfileKnowledgeProvider pkp; 327 private String changeVersion; 328 private List<String> files = new ArrayList<String>(); // files created as by-products in destDir 329 330 private Map<KnownLinkType, String> links = new HashMap<>(); 331 private Map<String, StringPair> namedLinks = new HashMap<>(); 332 private boolean addName = false; 333 private Map<String, String> typeMap = new HashMap<>(); // type aliases that can be resolved in Markdown type links (mainly for cross-version usage) 334 private int base64Limit = 1024; 335 private boolean shortPatientForm; 336 private String uniqueLocalPrefix; 337 private Set<String> anchors = new HashSet<>(); 338 private boolean unknownLocalReferencesNotLinks; 339 private IResourceLinkResolver resolveLinkResolver; 340 private boolean debug; 341 private DesignationMode designationMode; 342 private boolean noHeader; 343 private Set<ActorDefinition> actorWhiteList = new HashSet<>(); 344 private boolean trackNarrativeSource; 345 private KeyIssuer crossLinkKeyGen; 346 private int randomTracker; 347 private boolean testing; 348 private PackageInformation pi; 349 @Getter @Setter boolean showStandardsStatus; 350 351 /** 352 * 353 * @param workerContext - access to all related resources that might be needed 354 * @param pi - package information needed for certain Questionnaire rendering views 355 * @param markdown - appropriate markdown processing engine 356 * @param terminologyServiceOptions - options to use when looking up codes 357 * @param specLink - path to FHIR specification 358 * @param locale - i18n for rendering 359 */ 360 public RenderingContext(IWorkerContext workerContext, PackageInformation pi, MarkDownProcessor markdown, ValidationOptions terminologyServiceOptions, String specLink, String localPrefix, Locale locale, ResourceRendererMode mode, GenerationRules rules) { 361 super(); 362 this.worker = workerContext; 363 this.pi = pi; 364 this.markdown = markdown; 365 this.setLocale(locale); 366 this.links.put(KnownLinkType.SPEC, specLink); 367 this.localPrefix = localPrefix; 368 this.mode = mode; 369 this.rules = rules; 370 this.designationMode = DesignationMode.ALL; 371 if (terminologyServiceOptions != null) { 372 this.terminologyServiceOptions = terminologyServiceOptions; 373 } 374 crossLinkKeyGen = new KeyIssuer("xn"); 375 } 376 377 public RenderingContext(IWorkerContext workerContext, MarkDownProcessor markdown, ValidationOptions terminologyServiceOptions, String specLink, String localPrefix, Locale locale, ResourceRendererMode mode, GenerationRules rules) { 378 this(workerContext, null, markdown, terminologyServiceOptions, specLink, localPrefix, locale, mode, rules); 379 } 380 381 public RenderingContext copy(boolean copyAnchors) { 382 RenderingContext res = new RenderingContext(worker, pi, markdown, terminologyServiceOptions, getLink(KnownLinkType.SPEC, false), localPrefix, getLocale(), mode, rules); 383 384 res.resolver = resolver; 385 res.templateProvider = templateProvider; 386 res.services = services; 387 res.parser = parser; 388 389 res.headerLevelContext = headerLevelContext; 390 res.canonicalUrlsAsLinks = canonicalUrlsAsLinks; 391 res.pretty = pretty; 392 res.contained = contained; 393 394 res.noSlowLookup = noSlowLookup; 395 res.codeSystemPropList.addAll(codeSystemPropList); 396 397 res.profileUtilitiesR = profileUtilitiesR; 398 res.contextUtilities = contextUtilities; 399 res.definitionsTarget = definitionsTarget; 400 res.destDir = destDir; 401 res.scenarioMode = scenarioMode; 402 res.questionnaireMode = questionnaireMode; 403 res.structureMode = structureMode; 404 res.showSummaryTable = showSummaryTable; 405 res.links.putAll(links); 406 res.inlineGraphics = inlineGraphics; 407 res.timeZoneId = timeZoneId; 408 res.dateTimeFormat = dateTimeFormat; 409 res.dateFormat = dateFormat; 410 res.dateYearFormat = dateYearFormat; 411 res.dateYearMonthFormat = dateYearMonthFormat; 412 res.targetVersion = targetVersion; 413 res.showComments = showComments; 414 res.copyButton = copyButton; 415 res.pkp = pkp; 416 res.defaultStandardsStatus = defaultStandardsStatus; 417 res.changeVersion = changeVersion; 418 419 res.terminologyServiceOptions = terminologyServiceOptions.copy(); 420 res.typeMap.putAll(typeMap); 421 res.multiLanguagePolicy = multiLanguagePolicy; 422 res.allowedLanguages.addAll(allowedLanguages); 423 if (copyAnchors) { 424 res.anchors = anchors; 425 } 426 res.unknownLocalReferencesNotLinks = unknownLocalReferencesNotLinks; 427 res.resolveLinkResolver = resolveLinkResolver; 428 res.debug = debug; 429 res.noHeader = noHeader; 430 res.uniqueLocalPrefix = uniqueLocalPrefix; 431 res.secondaryLang = secondaryLang; 432 res.fixedFormat = fixedFormat; 433 res.oids = oids; 434 res.base64Limit = base64Limit; 435 res.shortPatientForm = shortPatientForm; 436 res.designationMode = designationMode; 437 res.addName = addName; 438 res.typeMap = typeMap; 439 res.trackNarrativeSource = trackNarrativeSource; 440 res.crossLinkKeyGen = crossLinkKeyGen; 441 442 res.getActorWhiteList().addAll(actorWhiteList); 443 444// not sure about these 445// private List<String> files = new ArrayList<String>(); // files created as by-products in destDir 446// private Map<KnownLinkType, String> links = new HashMap<>(); 447// private Map<String, StringPair> namedLinks = new HashMap<>(); 448 449 return res; 450 } 451 452 453 public IWorkerContext getContext() { 454 return worker; 455 } 456 457 public PackageInformation getPackageInformation() { 458 return pi; 459 } 460 461 // -- 2. Markdown support ------------------------------------------------------- 462 463 public ProfileUtilities getProfileUtilities() { 464 if (profileUtilitiesR == null) { 465 profileUtilitiesR = new ProfileUtilities(worker, null, pkp); 466 } 467 return profileUtilitiesR; 468 } 469 470 public IWorkerContext getWorker() { 471 return worker; 472 } 473 474 public boolean isCanonicalUrlsAsLinks() { 475 return canonicalUrlsAsLinks; 476 } 477 478 public RenderingContext setCanonicalUrlsAsLinks(boolean canonicalUrlsAsLinks) { 479 this.canonicalUrlsAsLinks = canonicalUrlsAsLinks; 480 return this; 481 } 482 483 public MarkDownProcessor getMarkdown() { 484 if (markdown == null) { 485 markdown = new MarkDownProcessor(Dialect.COMMON_MARK); 486 } 487 return markdown; 488 } 489 490 public MultiLanguagePolicy getMultiLanguagePolicy() { 491 return multiLanguagePolicy; 492 } 493 494 public void setMultiLanguagePolicy(MultiLanguagePolicy multiLanguagePolicy) { 495 this.multiLanguagePolicy = multiLanguagePolicy; 496 } 497 498 public Set<String> getAllowedLanguages() { 499 return allowedLanguages; 500 } 501 502 public String getLocalPrefix() { 503 return localPrefix; 504 } 505 506 public ValidationOptions getTerminologyServiceOptions() { 507 return terminologyServiceOptions; 508 } 509 510 public int getHeaderLevelContext() { 511 return headerLevelContext; 512 } 513 514 public RenderingContext setHeaderLevelContext(int headerLevelContext) { 515 this.headerLevelContext = headerLevelContext; 516 return this; 517 } 518 519 public IReferenceResolver getResolver() { 520 return resolver; 521 } 522 523 public RenderingContext setResolver(IReferenceResolver resolver) { 524 this.resolver = resolver; 525 return this; 526 } 527 528 public RenderingContext setTerminologyServiceOptions(ValidationOptions terminologyServiceOptions) { 529 this.terminologyServiceOptions = terminologyServiceOptions; 530 return this; 531 } 532 533 public boolean isNoSlowLookup() { 534 return noSlowLookup; 535 } 536 537 public RenderingContext setNoSlowLookup(boolean noSlowLookup) { 538 this.noSlowLookup = noSlowLookup; 539 return this; 540 } 541 542 public String getDefinitionsTarget() { 543 return definitionsTarget; 544 } 545 546 public RenderingContext setDefinitionsTarget(String definitionsTarget) { 547 this.definitionsTarget = definitionsTarget; 548 return this; 549 } 550 551 public String getDestDir() { 552 return destDir; 553 } 554 555 public RenderingContext setDestDir(String destDir) { 556 this.destDir = destDir; 557 return this; 558 } 559 560 public RenderingContext setProfileUtilities(ProfileUtilities profileUtilities) { 561 this.profileUtilitiesR = profileUtilities; 562 if (pkp == null && profileUtilities.getPkp() != null) { 563 pkp = profileUtilities.getPkp(); 564 } 565 return this; 566 } 567 568 public ILiquidTemplateProvider getTemplateProvider() { 569 return templateProvider; 570 } 571 572 public RenderingContext setTemplateProvider(ILiquidTemplateProvider templateProvider) { 573 this.templateProvider = templateProvider; 574 return this; 575 } 576 577 public IHostApplicationServices getServices() { 578 return services; 579 } 580 581 public RenderingContext setServices(IHostApplicationServices services) { 582 this.services = services; 583 return this; 584 } 585 586 public boolean isPretty() { 587 return pretty; 588 } 589 590 public RenderingContext setPretty(boolean pretty) { 591 this.pretty = pretty; 592 return this; 593 } 594 595 public ITypeParser getParser() { 596 return parser; 597 } 598 599 public RenderingContext setParser(ITypeParser parser) { 600 this.parser = parser; 601 return this; 602 } 603 604 605 public List<String> getCodeSystemPropList() { 606 return codeSystemPropList; 607 } 608 609 public RenderingContext setCodeSystemPropList(List<String> codeSystemPropList) { 610 this.codeSystemPropList = codeSystemPropList; 611 return this; 612 } 613 614 615 public boolean isInlineGraphics() { 616 return inlineGraphics; 617 } 618 619 public RenderingContext setInlineGraphics(boolean inlineGraphics) { 620 this.inlineGraphics = inlineGraphics; 621 return this; 622 } 623 624 public boolean isShowSummaryTable() { 625 return showSummaryTable; 626 } 627 628 public RenderingContext setShowSummaryTable(boolean header) { 629 this.showSummaryTable = header; 630 return this; 631 } 632 633 public ExampleScenarioRendererMode getScenarioMode() { 634 return scenarioMode; 635 } 636 637 public RenderingContext setScenarioMode(ExampleScenarioRendererMode scenarioMode) { 638 this.scenarioMode = scenarioMode; 639 return this; 640 } 641 642 public QuestionnaireRendererMode getQuestionnaireMode() { 643 return questionnaireMode; 644 } 645 646 public RenderingContext setQuestionnaireMode(QuestionnaireRendererMode questionnaireMode) { 647 this.questionnaireMode = questionnaireMode; 648 return this; 649 } 650 651 public StructureDefinitionRendererMode getStructureMode() { 652 return structureMode; 653 } 654 655 public RenderingContext setStructureMode(StructureDefinitionRendererMode structureMode) { 656 this.structureMode = structureMode; 657 return this; 658 } 659 660 public String fixReference(String ref) { 661 if (ref == null) { 662 return null; 663 } 664 if (!Utilities.isAbsoluteUrl(ref)) { 665 return (localPrefix == null ? "" : localPrefix)+ref; 666 } 667 if (ref.startsWith("http://hl7.org/fhir") && !ref.substring(20).contains("/")) { 668 return getLink(KnownLinkType.SPEC, true)+ref.substring(20); 669 } 670 return ref; 671 } 672 673 public RenderingContext setLocalPrefix(String localPrefix) { 674 this.localPrefix = localPrefix; 675 return this; 676 } 677 678 public FhirPublication getTargetVersion() { 679 return targetVersion; 680 } 681 682 public RenderingContext setTargetVersion(FhirPublication targetVersion) { 683 this.targetVersion = targetVersion; 684 return this; 685 } 686 687 public boolean isTechnicalMode() { 688 return mode == ResourceRendererMode.TECHNICAL; 689 } 690 691 /** 692 * if the timezone is null, the rendering will default to the source timezone 693 * in the resource 694 * 695 * Note that if you're working server side, the FHIR project recommends the use 696 * of the Date header so that clients know what timezone the server defaults to, 697 * 698 * There is no standard way for the server to know what the client timezone is. 699 * In the case where the client timezone is unknown, the timezone should be null 700 * 701 * @return the specified timezone to render in 702 */ 703 public ZoneId getTimeZoneId() { 704 return timeZoneId; 705 } 706 707 public RenderingContext setTimeZoneId(ZoneId timeZoneId) { 708 this.timeZoneId = timeZoneId; 709 return this; 710 } 711 712 713 /** 714 * In the absence of a specified format, the renderers will default to 715 * the FormatStyle.MEDIUM for the current locale. 716 * 717 * @return the format to use 718 */ 719 public DateTimeFormatter getDateTimeFormat() { 720 return this.dateTimeFormat; 721 } 722 723 public RenderingContext setDateTimeFormat(DateTimeFormatter dateTimeFormat) { 724 this.dateTimeFormat = dateTimeFormat; 725 return this; 726 } 727 728 public RenderingContext setDateTimeFormatString(String dateTimeFormat) { 729 this.dateTimeFormat = DateTimeFormatter.ofPattern(dateTimeFormat); 730 return this; 731 } 732 733 /** 734 * In the absence of a specified format, the renderers will default to 735 * the FormatStyle.MEDIUM for the current locale. 736 * 737 * @return the format to use 738 */ 739 public DateTimeFormatter getDateFormat() { 740 return this.dateFormat; 741 } 742 743 public RenderingContext setDateFormat(DateTimeFormatter dateFormat) { 744 this.dateFormat = dateFormat; 745 return this; 746 } 747 748 public RenderingContext setDateFormatString(String dateFormat) { 749 this.dateFormat = DateTimeFormatter.ofPattern(dateFormat); 750 return this; 751 } 752 753 public DateTimeFormatter getDateYearFormat() { 754 return dateYearFormat; 755 } 756 757 public RenderingContext setDateYearFormat(DateTimeFormatter dateYearFormat) { 758 this.dateYearFormat = dateYearFormat; 759 return this; 760 } 761 762 public RenderingContext setDateYearFormatString(String dateYearFormat) { 763 this.dateYearFormat = DateTimeFormatter.ofPattern(dateYearFormat); 764 return this; 765 } 766 767 public DateTimeFormatter getDateYearMonthFormat() { 768 return dateYearMonthFormat; 769 } 770 771 public RenderingContext setDateYearMonthFormat(DateTimeFormatter dateYearMonthFormat) { 772 this.dateYearMonthFormat = dateYearMonthFormat; 773 return this; 774 } 775 776 public RenderingContext setDateYearMonthFormatString(String dateYearMonthFormat) { 777 this.dateYearMonthFormat = DateTimeFormatter.ofPattern(dateYearMonthFormat); 778 return this; 779 } 780 781 public ResourceRendererMode getMode() { 782 return mode; 783 } 784 785 public RenderingContext setMode(ResourceRendererMode mode) { 786 this.mode = mode; 787 return this; 788 } 789 790 public boolean isContained() { 791 return contained; 792 } 793 794 public RenderingContext setContained(boolean contained) { 795 this.contained = contained; 796 return this; 797 } 798 public boolean isShowComments() { 799 return showComments; 800 } 801 public RenderingContext setShowComments(boolean showComments) { 802 this.showComments = showComments; 803 return this; 804 } 805 public boolean isCopyButton() { 806 return copyButton; 807 } 808 public RenderingContext setCopyButton(boolean copyButton) { 809 this.copyButton = copyButton; 810 return this; 811 } 812 813 public RenderingContext setPkp(ProfileKnowledgeProvider pkp) { 814 this.pkp = pkp; 815 return this; 816 } 817 public ProfileKnowledgeProvider getPkp() { 818 return pkp; 819 } 820 821 public boolean hasLink(KnownLinkType link) { 822 return links.containsKey(link); 823 } 824 825 public String getLink(KnownLinkType link, boolean secure) { 826 String url = links.get(link); 827 if (url != null && secure && url.startsWith("http://")) { 828 url = "https://" + url.substring("http://".length()); 829 } 830 return url; 831 } 832 public void addLink(KnownLinkType type, String link) { 833 links.put(type, link); 834 835 } 836 public GenerationRules getRules() { 837 return rules; 838 } 839 public RenderingContext setRules(GenerationRules rules) { 840 this.rules = rules; 841 return this; 842 } 843 public StandardsStatus getDefaultStandardsStatus() { 844 return defaultStandardsStatus; 845 } 846 public RenderingContext setDefaultStandardsStatus(StandardsStatus defaultStandardsStatus) { 847 this.defaultStandardsStatus = defaultStandardsStatus; 848 return this; 849 } 850 851 public String getChangeVersion() { 852 return changeVersion; 853 } 854 855 public RenderingContext setChangeVersion(String changeVersion) { 856 this.changeVersion = changeVersion; 857 return this; 858 } 859 860 public Map<String, StringPair> getNamedLinks() { 861 return namedLinks; 862 } 863 864 public void registerFile(String n) { 865 try { 866 files.add(Utilities.path(destDir, n)); 867 } catch (IOException e) { 868 } 869 } 870 871 public List<String> getFiles() { 872 return files; 873 } 874 875 public FixedValueFormat getFixedFormat() { 876 return fixedFormat; 877 } 878 879 public void setFixedFormat(FixedValueFormat fixedFormat) { 880 this.fixedFormat = fixedFormat; 881 } 882 883 public boolean isAddName() { 884 return addName; 885 } 886 887 public RenderingContext setAddName(boolean addName) { 888 this.addName = addName; 889 return this; 890 } 891 892 public Map<String, String> getTypeMap() { 893 return typeMap; 894 } 895 896 897 public String toStr(int v) { 898 NumberFormat nf = NumberFormat.getInstance(getLocale()); 899 return nf.format(v); 900 } 901 902 903 public String getTranslated(PrimitiveType<?>... tl) { 904 for (PrimitiveType<?> t : tl) { 905 StringType v = ExtensionUtilities.getLanguageTranslationElement(t, getLocale().toLanguageTag()); 906 if (v != null) { 907 return v.primitiveValue(); 908 } 909 } 910 for (PrimitiveType<?> t : tl) { 911 if (t.hasValue()) { 912 return t.primitiveValue(); 913 } 914 } 915 return null; 916 } 917 918 public String getTranslated(ResourceWrapper t) { 919 if (t == null) { 920 return null; 921 } 922 923 for (ResourceWrapper e : t.extensions(ExtensionDefinitions.EXT_TRANSLATION)) { 924 String l = e.extensionString("lang"); 925 if (l != null && l.equals(getLocale().toLanguageTag())) { 926 String v = e.extensionString("content"); 927 if (v != null) { 928 return v; 929 } 930 } 931 } 932 933 return t.primitiveValue(); 934 } 935 936 public StringType getTranslatedElement(PrimitiveType<?>... tl) { 937 for (PrimitiveType<?> t : tl) { 938 StringType v = ExtensionUtilities.getLanguageTranslationElement(t, getLocale().toLanguageTag()); 939 if (v != null) { 940 return v; 941 } 942 } 943 for (PrimitiveType<?> t : tl) { 944 if (t.hasValue()) { 945 if (t instanceof StringType) { 946 return (StringType) t; 947 } else { 948 return new StringType(t.asStringValue()); 949 } 950 } 951 } 952 return null; 953 } 954 955 956 public String getTranslatedCode(Base b, String codeSystem) { 957 if (b instanceof org.hl7.fhir.r5.model.Element) { 958 org.hl7.fhir.r5.model.Element e = (org.hl7.fhir.r5.model.Element) b; 959 String v = ExtensionUtilities.getLanguageTranslation(e, getLocale().toLanguageTag()); 960 if (v != null) { 961 return v; 962 } 963 // no? then see if the tx service can translate it for us 964 try { 965 ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(getLocale().toLanguageTag()).withVersionFlexible(true), 966 codeSystem, null, e.primitiveValue(), null); 967 if (t.isOk() && t.getDisplay() != null) { 968 return t.getDisplay(); 969 } 970 } catch (Exception ex) { 971 // Do nothing. 972 } 973 if (e instanceof Enumeration<?>) { 974 return ((Enumeration<?>) e).getDisplay(); 975 } else { 976 return e.primitiveValue(); 977 } 978 } else if (b instanceof Element) { 979 return getTranslatedCode((Element) b, codeSystem); 980 } else { 981 return "??"; 982 } 983 } 984 985 public String getTranslatedCode(String code, String codeSystem) { 986 try { 987 ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(getLocale().toLanguageTag()).withVersionFlexible(true), codeSystem, null, code, null); 988 if (t.isOk() && t.getDisplay() != null) { 989 return t.getDisplay(); 990 } else { 991 return code; 992 } 993 } catch (Exception ex) { 994 return code; 995 } 996 } 997 998 public String getTranslatedCode(Enumeration<?> e, String codeSystem) { 999 String v = ExtensionUtilities.getLanguageTranslation(e, getLocale().toLanguageTag()); 1000 if (v != null) { 1001 return v; 1002 } 1003 // no? then see if the tx service can translate it for us 1004 try { 1005 ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(getLocale().toLanguageTag()).withVersionFlexible(true), 1006 codeSystem, null, e.getCode(), null); 1007 if (t.isOk() && t.getDisplay() != null) { 1008 return t.getDisplay(); 1009 } 1010 } catch (Exception ex) { 1011 // nothing 1012 } 1013 1014 try { 1015 ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withVersionFlexible(true), 1016 codeSystem, null, e.getCode(), null); 1017 if (t.isOk() && t.getDisplay() != null) { 1018 return t.getDisplay(); 1019 } 1020 } catch (Exception ex) { 1021 // nothing 1022 } 1023 1024 return e.getCode(); 1025 } 1026 1027 public String getTranslatedCode(Element e, String codeSystem) { 1028 1029 // first we look through the translation extensions 1030 for (Element ext : e.getChildrenByName("extension")) { 1031 String url = ext.getNamedChildValue("url"); 1032 if (url.equals(ExtensionDefinitions.EXT_TRANSLATION)) { 1033 Base e1 = ext.getExtensionValue("lang"); 1034 1035 if (e1 != null && e1.primitiveValue() != null && e1.primitiveValue().equals(getLocale().toLanguageTag())) { 1036 e1 = ext.getExtensionValue("content"); 1037 if (e1 != null && e1.isPrimitive()) { 1038 return e1.primitiveValue(); 1039 } 1040 } 1041 } 1042 1043 // no? then see if the tx service can translate it for us 1044 try { 1045 ValidationResult t = getContext().validateCode(getTerminologyServiceOptions().withLanguage(getLocale().toLanguageTag()).withVersionFlexible(true), 1046 codeSystem, null, e.primitiveValue(), null); 1047 if (t.isOk() && t.getDisplay() != null) { 1048 return t.getDisplay(); 1049 } 1050 } catch (Exception ex) { 1051 // nothing 1052 } 1053 } 1054 return e.primitiveValue(); 1055 } 1056 1057 public RenderingContext withLocale(Locale locale) { 1058 setLocale(locale); 1059 return this; 1060 } 1061 1062 public RenderingContext withLocaleCode(String locale) { 1063 setLocale(Locale.forLanguageTag(locale)); 1064 return this; 1065 } 1066 1067 public RenderingContext withMode(ResourceRendererMode mode) { 1068 setMode(mode); 1069 return this; 1070 } 1071 1072 public ContextUtilities getContextUtilities() { 1073 if (contextUtilities == null) { 1074 contextUtilities = new ContextUtilities(worker); 1075 } 1076 return contextUtilities; 1077 } 1078 1079 public int getBase64Limit() { 1080 return base64Limit; 1081 } 1082 1083 public void setBase64Limit(int base64Limit) { 1084 this.base64Limit = base64Limit; 1085 } 1086 1087 public boolean isShortPatientForm() { 1088 return shortPatientForm; 1089 } 1090 1091 public void setShortPatientForm(boolean shortPatientForm) { 1092 this.shortPatientForm = shortPatientForm; 1093 } 1094 1095 public boolean isSecondaryLang() { 1096 return secondaryLang; 1097 } 1098 1099 public void setSecondaryLang(boolean secondaryLang) { 1100 this.secondaryLang = secondaryLang; 1101 } 1102 1103 public String prefixAnchor(String anchor) { 1104 return uniqueLocalPrefix == null ? anchor : uniqueLocalPrefix+"-" + anchor; 1105 } 1106 1107 public String prefixLocalHref(String url) { 1108 if (url == null || uniqueLocalPrefix == null || !url.startsWith("#")) { 1109 return url; 1110 } 1111 return "#"+uniqueLocalPrefix+"-"+url.substring(1); 1112 } 1113 1114 public String getUniqueLocalPrefix() { 1115 return uniqueLocalPrefix; 1116 } 1117 1118 public void setUniqueLocalPrefix(String uniqueLocalPrefix) { 1119 this.uniqueLocalPrefix = uniqueLocalPrefix; 1120 } 1121 1122 public RenderingContext withUniqueLocalPrefix(String uniqueLocalPrefix) { 1123 RenderingContext self = this.copy(true); 1124 self.uniqueLocalPrefix = uniqueLocalPrefix; 1125 return self; 1126 } 1127 1128 public RenderingContext forContained() { 1129 RenderingContext self = this.copy(true); 1130 self.contained = true; 1131 return self; 1132 } 1133 1134 public boolean hasAnchor(String anchor) { 1135 return anchors.contains(anchor); 1136 } 1137 1138 public void addAnchor(String anchor) { 1139 anchors.add(anchor); 1140 } 1141 1142 public Set<String> getAnchors() { 1143 return anchors; 1144 } 1145 1146 public void clearAnchors() { 1147 anchors.clear(); 1148 } 1149 1150 public boolean isUnknownLocalReferencesNotLinks() { 1151 return unknownLocalReferencesNotLinks; 1152 } 1153 1154 public void setUnknownLocalReferencesNotLinks(boolean unknownLocalReferencesNotLinks) { 1155 this.unknownLocalReferencesNotLinks = unknownLocalReferencesNotLinks; 1156 } 1157 1158 public <T extends Resource> T findLinkableResource(Class<T> class_, String uri) throws IOException { 1159 if (resolveLinkResolver == null) { 1160 return null; 1161 } else { 1162 return resolveLinkResolver.findLinkableResource(class_, uri); 1163 } 1164 } 1165 1166 public IResourceLinkResolver getResolveLinkResolver() { 1167 return resolveLinkResolver; 1168 } 1169 1170 public void setResolveLinkResolver(IResourceLinkResolver resolveLinkResolver) { 1171 this.resolveLinkResolver = resolveLinkResolver; 1172 } 1173 1174 public boolean isDebug() { 1175 return debug; 1176 } 1177 1178 public void setDebug(boolean debug) { 1179 this.debug = debug; 1180 } 1181 1182 public DesignationMode getDesignationMode() { 1183 return designationMode; 1184 } 1185 1186 public void setDesignationMode(DesignationMode designationMode) { 1187 this.designationMode = designationMode; 1188 } 1189 1190 public boolean isOids() { 1191 return oids; 1192 } 1193 1194 public void setOids(boolean oids) { 1195 this.oids = oids; 1196 } 1197 1198 public RenderingContext withOids(boolean oids) { 1199 RenderingContext self = this.copy(false); 1200 self.oids = oids; 1201 return self; 1202 } 1203 1204 public boolean isNoHeader() { 1205 return noHeader; 1206 } 1207 1208 public void setNoHeader(boolean noHeader) { 1209 this.noHeader = noHeader; 1210 } 1211 1212 public Set<ActorDefinition> getActorWhiteList() { 1213 return actorWhiteList; 1214 } 1215 1216 public boolean isTrackNarrativeSource() { 1217 return trackNarrativeSource; 1218 } 1219 1220 public void setTrackNarrativeSource(boolean trackNarrativeSource) { 1221 this.trackNarrativeSource = trackNarrativeSource; 1222 } 1223 1224 public String nextXNKey() { 1225 return crossLinkKeyGen.issueKey(); 1226 } 1227 1228 public String getRandomName(String id) { 1229 if (testing) { 1230 return id+"-"+(++randomTracker); 1231 } else { 1232 return UUID.randomUUID().toString().toLowerCase(); 1233 } 1234 } 1235 1236 public boolean isTesting() { 1237 return testing; 1238 } 1239 1240 /** 1241 * testing is used to turn off production of random UUIDs and produce something known and predictable but 1242 * likely to produce name clashes in production - for the sake of test case reproducibility 1243 * @param testing 1244 */ 1245 public void setTesting(boolean testing) { 1246 this.testing = testing; 1247 } 1248 1249 public boolean forValidResource() { 1250 return getRules() == GenerationRules.VALID_RESOURCE; 1251 } 1252 1253 public boolean forPublisher() { 1254 return getRules() == GenerationRules.IG_PUBLISHER; 1255 } 1256 1257 1258}