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