
001package org.hl7.fhir.r5.renderers.utils; 002 003import java.io.IOException; 004import java.text.DateFormat; 005import java.time.ZoneId; 006import java.time.format.DateTimeFormatter; 007import java.time.format.FormatStyle; 008import java.util.ArrayList; 009import java.util.List; 010import java.util.Locale; 011import java.util.TimeZone; 012 013import org.hl7.fhir.exceptions.FHIRException; 014import org.hl7.fhir.exceptions.FHIRFormatError; 015import org.hl7.fhir.r5.conformance.ProfileUtilities; 016import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider; 017import org.hl7.fhir.r5.context.IWorkerContext; 018import org.hl7.fhir.r5.model.Base; 019import org.hl7.fhir.r5.model.DomainResource; 020import org.hl7.fhir.r5.model.Enumerations.FHIRVersion; 021import org.hl7.fhir.r5.model.FhirPublication; 022import org.hl7.fhir.r5.renderers.utils.Resolver.IReferenceResolver; 023import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; 024import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; 025import org.hl7.fhir.utilities.MarkDownProcessor; 026import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; 027import org.hl7.fhir.utilities.Utilities; 028import org.hl7.fhir.utilities.validation.ValidationOptions; 029 030public class RenderingContext { 031 032 // provides liquid templates, if they are available for the content 033 public interface ILiquidTemplateProvider { 034 String findTemplate(RenderingContext rcontext, DomainResource r); 035 String findTemplate(RenderingContext rcontext, String resourceName); 036 } 037 038 // parses xml to an XML instance. Whatever codes provides this needs to provide something that parses the right version 039 public interface ITypeParser { 040 Base parseType(String xml, String type) throws FHIRFormatError, IOException, FHIRException ; 041 } 042 043 /** 044 * What kind of user the renderer is targeting - end users, or technical users 045 * 046 * This affects the way codes and references are rendered 047 * 048 * @author graha 049 * 050 */ 051 public enum ResourceRendererMode { 052 /** 053 * The user has no interest in the contents of the FHIR resource, and just wants to see the data 054 * 055 */ 056 END_USER, 057 058 /** 059 * The user wants to see the resource, but a technical view so they can see what's going on with the content 060 */ 061 TECHNICAL 062 } 063 064 public enum QuestionnaireRendererMode { 065 /** 066 * A visual presentation of the questionnaire, with a set of property panes that can be toggled on and off. 067 * Note that this is not the same as how the questionnaire would like on a form filler, since all dynamic behavior is ignored 068 */ 069 FORM, 070 071 /** 072 * a structured tree that presents the content of the questionnaire in a logical fashion 073 */ 074 TREE, 075 076 /** 077 * A structured tree that presents the enableWhen, terminology and expression bindings for the questionnaire 078 */ 079 LOGIC, 080 081 /** 082 * A presentation that lists all the items, with full details about them 083 */ 084 DEFNS, 085 086 /** 087 * Rendered links to various openly available Form Filler applications that know how to render a questionnaire published in a package 088 */ 089 LINKS 090 } 091 092 private IWorkerContext worker; 093 private MarkDownProcessor markdown; 094 private ResourceRendererMode mode; 095 private IReferenceResolver resolver; 096 private ILiquidTemplateProvider templateProvider; 097 private IEvaluationContext services; 098 private ITypeParser parser; 099 100 private String lang; 101 private String localPrefix; // relative link within local context 102 private String specificationLink; 103 private String selfLink; // absolute link to where the content is to be found (only used in a few circumstances when making external references to tools) 104 private int headerLevelContext; 105 private boolean canonicalUrlsAsLinks; 106 private boolean pretty; 107 private boolean header; 108 109 private ValidationOptions terminologyServiceOptions = new ValidationOptions(); 110 private boolean noSlowLookup; 111 private String tooCostlyNoteEmpty; 112 private String tooCostlyNoteNotEmpty; 113 private String tooCostlyNoteEmptyDependent; 114 private String tooCostlyNoteNotEmptyDependent; 115 private List<String> codeSystemPropList = new ArrayList<>(); 116 117 private ProfileUtilities profileUtilities; 118 private String definitionsTarget; 119 private String destDir; 120 private boolean inlineGraphics; 121 122 private QuestionnaireRendererMode questionnaireMode = QuestionnaireRendererMode.FORM; 123 private boolean addGeneratedNarrativeHeader = true; 124 125 private FhirPublication targetVersion; 126 private Locale locale; 127 private ZoneId timeZoneId; 128 private DateTimeFormatter dateTimeFormat; 129 private DateTimeFormatter dateFormat; 130 private DateTimeFormatter dateYearFormat; 131 private DateTimeFormatter dateYearMonthFormat; 132 133 /** 134 * 135 * @param context - access to all related resources that might be needed 136 * @param markdown - appropriate markdown processing engine 137 * @param terminologyServiceOptions - options to use when looking up codes 138 * @param specLink - path to FHIR specification 139 * @param lang - langauage to render in 140 */ 141 public RenderingContext(IWorkerContext worker, MarkDownProcessor markdown, ValidationOptions terminologyServiceOptions, String specLink, String localPrefix, String lang, ResourceRendererMode mode) { 142 super(); 143 this.worker = worker; 144 this.markdown = markdown; 145 this.lang = lang; 146 this.specificationLink = specLink; 147 this.localPrefix = localPrefix; 148 this.mode = mode; 149 if (terminologyServiceOptions != null) { 150 this.terminologyServiceOptions = terminologyServiceOptions; 151 } 152 // default to US locale - discussion here: https://github.com/hapifhir/org.hl7.fhir.core/issues/666 153 this.locale = new Locale.Builder().setLanguageTag("en-US").build(); 154 profileUtilities = new ProfileUtilities(worker, null, null); 155 } 156 157 public IWorkerContext getContext() { 158 return worker; 159 } 160 161 // -- 2. Markdown support ------------------------------------------------------- 162 163 public ProfileUtilities getProfileUtilities() { 164 return profileUtilities; 165 } 166 167 public IWorkerContext getWorker() { 168 return worker; 169 } 170 171 public boolean isCanonicalUrlsAsLinks() { 172 return canonicalUrlsAsLinks; 173 } 174 175 public RenderingContext setCanonicalUrlsAsLinks(boolean canonicalUrlsAsLinks) { 176 this.canonicalUrlsAsLinks = canonicalUrlsAsLinks; 177 return this; 178 } 179 180 public MarkDownProcessor getMarkdown() { 181 if (markdown == null) { 182 markdown = new MarkDownProcessor(Dialect.COMMON_MARK); 183 } 184 return markdown; 185 } 186 187 public String getLang() { 188 return lang; 189 } 190 191 public String getSpecificationLink() { 192 return specificationLink; 193 } 194 195 public String getLocalPrefix() { 196 return localPrefix; 197 } 198 199 public ValidationOptions getTerminologyServiceOptions() { 200 return terminologyServiceOptions; 201 } 202 203 204 public String getTooCostlyNoteEmpty() { 205 return tooCostlyNoteEmpty; 206 } 207 208 public RenderingContext setTooCostlyNoteEmpty(String tooCostlyNoteEmpty) { 209 this.tooCostlyNoteEmpty = tooCostlyNoteEmpty; 210 return this; 211 } 212 213 public String getTooCostlyNoteNotEmpty() { 214 return tooCostlyNoteNotEmpty; 215 } 216 217 public RenderingContext setTooCostlyNoteNotEmpty(String tooCostlyNoteNotEmpty) { 218 this.tooCostlyNoteNotEmpty = tooCostlyNoteNotEmpty; 219 return this; 220 } 221 222 public String getTooCostlyNoteEmptyDependent() { 223 return tooCostlyNoteEmptyDependent; 224 } 225 226 public RenderingContext setTooCostlyNoteEmptyDependent(String tooCostlyNoteEmptyDependent) { 227 this.tooCostlyNoteEmptyDependent = tooCostlyNoteEmptyDependent; 228 return this; 229 } 230 231 public String getTooCostlyNoteNotEmptyDependent() { 232 return tooCostlyNoteNotEmptyDependent; 233 } 234 235 public RenderingContext setTooCostlyNoteNotEmptyDependent(String tooCostlyNoteNotEmptyDependent) { 236 this.tooCostlyNoteNotEmptyDependent = tooCostlyNoteNotEmptyDependent; 237 return this; 238 } 239 240 public int getHeaderLevelContext() { 241 return headerLevelContext; 242 } 243 244 public RenderingContext setHeaderLevelContext(int headerLevelContext) { 245 this.headerLevelContext = headerLevelContext; 246 return this; 247 } 248 249 public IReferenceResolver getResolver() { 250 return resolver; 251 } 252 253 public RenderingContext setResolver(IReferenceResolver resolver) { 254 this.resolver = resolver; 255 return this; 256 } 257 258 public RenderingContext setTerminologyServiceOptions(ValidationOptions terminologyServiceOptions) { 259 this.terminologyServiceOptions = terminologyServiceOptions; 260 return this; 261 } 262 263 public boolean isNoSlowLookup() { 264 return noSlowLookup; 265 } 266 267 public RenderingContext setNoSlowLookup(boolean noSlowLookup) { 268 this.noSlowLookup = noSlowLookup; 269 return this; 270 } 271 272 public String getDefinitionsTarget() { 273 return definitionsTarget; 274 } 275 276 public RenderingContext setDefinitionsTarget(String definitionsTarget) { 277 this.definitionsTarget = definitionsTarget; 278 return this; 279 } 280 281 public String getDestDir() { 282 return destDir; 283 } 284 285 public RenderingContext setDestDir(String destDir) { 286 this.destDir = destDir; 287 return this; 288 } 289 290 public RenderingContext setProfileUtilities(ProfileUtilities profileUtilities) { 291 this.profileUtilities = profileUtilities; 292 return this; 293 } 294 295 public ILiquidTemplateProvider getTemplateProvider() { 296 return templateProvider; 297 } 298 299 public RenderingContext setTemplateProvider(ILiquidTemplateProvider templateProvider) { 300 this.templateProvider = templateProvider; 301 return this; 302 } 303 304 public IEvaluationContext getServices() { 305 return services; 306 } 307 308 public RenderingContext setServices(IEvaluationContext services) { 309 this.services = services; 310 return this; 311 } 312 313 public boolean isPretty() { 314 return pretty; 315 } 316 317 public RenderingContext setPretty(boolean pretty) { 318 this.pretty = pretty; 319 return this; 320 } 321 322 public ITypeParser getParser() { 323 return parser; 324 } 325 326 public RenderingContext setParser(ITypeParser parser) { 327 this.parser = parser; 328 return this; 329 } 330 331 332 public List<String> getCodeSystemPropList() { 333 return codeSystemPropList; 334 } 335 336 public RenderingContext setCodeSystemPropList(List<String> codeSystemPropList) { 337 this.codeSystemPropList = codeSystemPropList; 338 return this; 339 } 340 341 public RenderingContext copy() { 342 RenderingContext res = new RenderingContext(worker, markdown, terminologyServiceOptions, specificationLink, localPrefix, lang, mode); 343 344 res.resolver = resolver; 345 res.templateProvider = templateProvider; 346 res.services = services; 347 res.parser = parser; 348 349 res.headerLevelContext = headerLevelContext; 350 res.canonicalUrlsAsLinks = canonicalUrlsAsLinks; 351 res.pretty = pretty; 352 353 res.noSlowLookup = noSlowLookup; 354 res.tooCostlyNoteEmpty = tooCostlyNoteEmpty; 355 res.tooCostlyNoteNotEmpty = tooCostlyNoteNotEmpty; 356 res.tooCostlyNoteEmptyDependent = tooCostlyNoteEmptyDependent; 357 res.tooCostlyNoteNotEmptyDependent = tooCostlyNoteNotEmptyDependent; 358 res.codeSystemPropList.addAll(codeSystemPropList); 359 360 res.profileUtilities = profileUtilities; 361 res.definitionsTarget = definitionsTarget; 362 res.destDir = destDir; 363 res.addGeneratedNarrativeHeader = addGeneratedNarrativeHeader; 364 res.questionnaireMode = questionnaireMode; 365 res.header = header; 366 res.selfLink = selfLink; 367 res.inlineGraphics = inlineGraphics; 368 res.timeZoneId = timeZoneId; 369 res.dateTimeFormat = dateTimeFormat; 370 res.dateFormat = dateFormat; 371 res.dateYearFormat = dateYearFormat; 372 res.dateYearMonthFormat = dateYearMonthFormat; 373 374 return res; 375 } 376 377 public boolean isInlineGraphics() { 378 return inlineGraphics; 379 } 380 381 public RenderingContext setInlineGraphics(boolean inlineGraphics) { 382 this.inlineGraphics = inlineGraphics; 383 return this; 384 } 385 386 public boolean isHeader() { 387 return header; 388 } 389 390 public RenderingContext setHeader(boolean header) { 391 this.header = header; 392 return this; 393 } 394 395 public QuestionnaireRendererMode getQuestionnaireMode() { 396 return questionnaireMode; 397 } 398 399 public RenderingContext setQuestionnaireMode(QuestionnaireRendererMode questionnaireMode) { 400 this.questionnaireMode = questionnaireMode; 401 return this; 402 } 403 404 public String getSelfLink() { 405 return selfLink; 406 } 407 408 public RenderingContext setSelfLink(String selfLink) { 409 this.selfLink = selfLink; 410 return this; 411 } 412 413 public String fixReference(String ref) { 414 if (!Utilities.isAbsoluteUrl(ref)) { 415 return (localPrefix == null ? "" : localPrefix)+ref; 416 } 417 if (ref.startsWith("http://hl7.org/fhir") && !ref.substring(20).contains("/")) { 418 return specificationLink+ref.substring(20); 419 } 420 return ref; 421 } 422 423 public RenderingContext setLang(String lang) { 424 this.lang = lang; 425 return this; 426 } 427 428 public RenderingContext setLocalPrefix(String localPrefix) { 429 this.localPrefix = localPrefix; 430 return this; 431 } 432 433 public boolean isAddGeneratedNarrativeHeader() { 434 return addGeneratedNarrativeHeader; 435 } 436 437 public RenderingContext setAddGeneratedNarrativeHeader(boolean addGeneratedNarrativeHeader) { 438 this.addGeneratedNarrativeHeader = addGeneratedNarrativeHeader; 439 return this; 440 } 441 442 public FhirPublication getTargetVersion() { 443 return targetVersion; 444 } 445 446 public void setTargetVersion(FhirPublication targetVersion) { 447 this.targetVersion = targetVersion; 448 } 449 450 public boolean isTechnicalMode() { 451 return mode == ResourceRendererMode.TECHNICAL; 452 } 453 454 public boolean hasLocale() { 455 return locale != null; 456 } 457 458 public Locale getLocale() { 459 if (locale == null) { 460 return Locale.getDefault(); 461 } else { 462 return locale; 463 } 464 } 465 466 public void setLocale(Locale locale) { 467 this.locale = locale; 468 } 469 470 471 /** 472 * if the timezone is null, the rendering will default to the source timezone 473 * in the resource 474 * 475 * Note that if you're working server side, the FHIR project recommends the use 476 * of the Date header so that clients know what timezone the server defaults to, 477 * 478 * There is no standard way for the server to know what the client timezone is. 479 * In the case where the client timezone is unknown, the timezone should be null 480 * 481 * @return the specified timezone to render in 482 */ 483 public ZoneId getTimeZoneId() { 484 return timeZoneId; 485 } 486 487 public void setTimeZoneId(ZoneId timeZoneId) { 488 this.timeZoneId = timeZoneId; 489 } 490 491 492 /** 493 * In the absence of a specified format, the renderers will default to 494 * the FormatStyle.MEDIUM for the current locale. 495 * 496 * @return the format to use 497 */ 498 public DateTimeFormatter getDateTimeFormat() { 499 return this.dateTimeFormat; 500 } 501 502 public void setDateTimeFormat(DateTimeFormatter dateTimeFormat) { 503 this.dateTimeFormat = dateTimeFormat; 504 } 505 506 public void setDateTimeFormatString(String dateTimeFormat) { 507 this.dateTimeFormat = DateTimeFormatter.ofPattern(dateTimeFormat); 508 } 509 510 /** 511 * In the absence of a specified format, the renderers will default to 512 * the FormatStyle.MEDIUM for the current locale. 513 * 514 * @return the format to use 515 */ 516 public DateTimeFormatter getDateFormat() { 517 return this.dateFormat; 518 } 519 520 public void setDateFormat(DateTimeFormatter dateFormat) { 521 this.dateFormat = dateFormat; 522 } 523 524 public void setDateFormatString(String dateFormat) { 525 this.dateFormat = DateTimeFormatter.ofPattern(dateFormat); 526 } 527 528 public DateTimeFormatter getDateYearFormat() { 529 return dateYearFormat; 530 } 531 532 public void setDateYearFormat(DateTimeFormatter dateYearFormat) { 533 this.dateYearFormat = dateYearFormat; 534 } 535 536 public void setDateYearFormatString(String dateYearFormat) { 537 this.dateYearFormat = DateTimeFormatter.ofPattern(dateYearFormat); 538 } 539 540 public DateTimeFormatter getDateYearMonthFormat() { 541 return dateYearMonthFormat; 542 } 543 544 public void setDateYearMonthFormat(DateTimeFormatter dateYearMonthFormat) { 545 this.dateYearMonthFormat = dateYearMonthFormat; 546 } 547 548 public void setDateYearMonthFormatString(String dateYearMonthFormat) { 549 this.dateYearMonthFormat = DateTimeFormatter.ofPattern(dateYearMonthFormat); 550 } 551 552 public ResourceRendererMode getMode() { 553 return mode; 554 } 555 556 public void setMode(ResourceRendererMode mode) { 557 this.mode = mode; 558 } 559 560 561}