001package org.hl7.fhir.r5.renderers; 002 003import static java.time.temporal.ChronoField.MONTH_OF_YEAR; 004import static java.time.temporal.ChronoField.YEAR; 005 006import java.io.IOException; 007import java.math.BigDecimal; 008import java.text.NumberFormat; 009import java.time.LocalDate; 010import java.time.ZoneId; 011import java.time.ZonedDateTime; 012import java.time.format.DateTimeFormatter; 013import java.time.format.DateTimeFormatterBuilder; 014import java.time.format.FormatStyle; 015import java.time.format.SignStyle; 016import java.util.Currency; 017import java.util.List; 018 019import org.hl7.fhir.exceptions.DefinitionException; 020import org.hl7.fhir.exceptions.FHIRException; 021import org.hl7.fhir.exceptions.FHIRFormatError; 022import org.hl7.fhir.r5.context.ContextUtilities; 023import org.hl7.fhir.r5.context.IWorkerContext; 024import org.hl7.fhir.r5.elementmodel.Element; 025import org.hl7.fhir.r5.model.Address; 026import org.hl7.fhir.r5.model.Annotation; 027import org.hl7.fhir.r5.model.BackboneType; 028import org.hl7.fhir.r5.model.Base; 029import org.hl7.fhir.r5.model.Base64BinaryType; 030import org.hl7.fhir.r5.model.BaseDateTimeType; 031import org.hl7.fhir.r5.model.CanonicalResource; 032import org.hl7.fhir.r5.model.CanonicalType; 033import org.hl7.fhir.r5.model.CodeSystem; 034import org.hl7.fhir.r5.model.CodeType; 035import org.hl7.fhir.r5.model.CodeableConcept; 036import org.hl7.fhir.r5.model.CodeableReference; 037import org.hl7.fhir.r5.model.Coding; 038import org.hl7.fhir.r5.model.ContactDetail; 039import org.hl7.fhir.r5.model.ContactPoint; 040import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem; 041import org.hl7.fhir.r5.model.DataRequirement; 042import org.hl7.fhir.r5.model.DataRequirement.DataRequirementCodeFilterComponent; 043import org.hl7.fhir.r5.model.DataRequirement.DataRequirementDateFilterComponent; 044import org.hl7.fhir.r5.model.DataRequirement.DataRequirementSortComponent; 045import org.hl7.fhir.r5.model.DataRequirement.SortDirection; 046import org.hl7.fhir.r5.model.DataType; 047import org.hl7.fhir.r5.model.DateTimeType; 048import org.hl7.fhir.r5.model.DateType; 049import org.hl7.fhir.r5.model.ElementDefinition; 050import org.hl7.fhir.r5.model.Enumeration; 051import org.hl7.fhir.r5.model.Expression; 052import org.hl7.fhir.r5.model.Extension; 053import org.hl7.fhir.r5.model.ExtensionHelper; 054import org.hl7.fhir.r5.model.HumanName; 055import org.hl7.fhir.r5.model.HumanName.NameUse; 056import org.hl7.fhir.r5.model.IdType; 057import org.hl7.fhir.r5.model.Identifier; 058import org.hl7.fhir.r5.model.MarkdownType; 059import org.hl7.fhir.r5.model.Money; 060import org.hl7.fhir.r5.model.NamingSystem; 061import org.hl7.fhir.r5.model.Period; 062import org.hl7.fhir.r5.model.PrimitiveType; 063import org.hl7.fhir.r5.model.Quantity; 064import org.hl7.fhir.r5.model.Range; 065import org.hl7.fhir.r5.model.Reference; 066import org.hl7.fhir.r5.model.Resource; 067import org.hl7.fhir.r5.model.SampledData; 068import org.hl7.fhir.r5.model.StringType; 069import org.hl7.fhir.r5.model.StructureDefinition; 070import org.hl7.fhir.r5.model.Timing; 071import org.hl7.fhir.r5.model.Timing.EventTiming; 072import org.hl7.fhir.r5.model.Timing.TimingRepeatComponent; 073import org.hl7.fhir.r5.model.Timing.UnitsOfTime; 074import org.hl7.fhir.r5.model.TriggerDefinition; 075import org.hl7.fhir.r5.model.UriType; 076import org.hl7.fhir.r5.model.UsageContext; 077import org.hl7.fhir.r5.model.ValueSet; 078import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; 079import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent; 080import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; 081import org.hl7.fhir.r5.renderers.CodeResolver.CodeResolution; 082import org.hl7.fhir.r5.renderers.utils.RenderingContext; 083import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; 084import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; 085import org.hl7.fhir.r5.terminologies.JurisdictionUtilities; 086import org.hl7.fhir.r5.terminologies.utilities.ValidationResult; 087import org.hl7.fhir.r5.utils.ToolingExtensions; 088import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 089import org.hl7.fhir.utilities.Utilities; 090import org.hl7.fhir.utilities.VersionUtilities; 091import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; 092import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece; 093import org.hl7.fhir.utilities.xhtml.NodeType; 094import org.hl7.fhir.utilities.xhtml.XhtmlNode; 095import org.hl7.fhir.utilities.xhtml.XhtmlParser; 096 097import ca.uhn.fhir.model.api.TemporalPrecisionEnum; 098 099public class DataRenderer extends Renderer implements CodeResolver { 100 101 // -- 1. context -------------------------------------------------------------- 102 103 public DataRenderer(RenderingContext context) { 104 super(context); 105 } 106 107 public DataRenderer(IWorkerContext worker) { 108 super(worker); 109 } 110 111 // -- 2. Markdown support ------------------------------------------------------- 112 113 public static String processRelativeUrls(String markdown, String path) { 114 if (markdown == null) { 115 return ""; 116 } 117 if (!Utilities.isAbsoluteUrl(path)) { 118 return markdown; 119 } 120 String basePath = path.contains("/") ? path.substring(0, path.lastIndexOf("/")+1) : path+"/"; 121 StringBuilder b = new StringBuilder(); 122 int i = 0; 123 while (i < markdown.length()) { 124 if (i < markdown.length()-3 && markdown.substring(i, i+2).equals("](")) { 125 int j = i + 2; 126 while (j < markdown.length() && markdown.charAt(j) != ')') 127 j++; 128 if (j < markdown.length()) { 129 String url = markdown.substring(i+2, j); 130 if (!Utilities.isAbsoluteUrl(url) && !url.startsWith("..")) { 131 // it's relative - so it's relative to the base URL 132 b.append("]("); 133 b.append(basePath); 134 } else { 135 b.append("]("); 136 } 137 i = i + 1; 138 } else 139 b.append(markdown.charAt(i)); 140 } else { 141 b.append(markdown.charAt(i)); 142 } 143 i++; 144 } 145 return b.toString(); 146 } 147 148 protected void addMarkdown(XhtmlNode x, String text, String path) throws FHIRFormatError, IOException, DefinitionException { 149 addMarkdown(x, processRelativeUrls(text, path)); 150 } 151 152 protected void addMarkdown(XhtmlNode x, String text) throws FHIRFormatError, IOException, DefinitionException { 153 if (text != null) { 154 // 1. custom FHIR extensions 155 while (text.contains("[[[")) { 156 String left = text.substring(0, text.indexOf("[[[")); 157 String link = text.substring(text.indexOf("[[[")+3, text.indexOf("]]]")); 158 String right = text.substring(text.indexOf("]]]")+3); 159 String path = null; 160 String url = link; 161 String[] parts = link.split("\\#"); 162 if (parts[0].contains(".")) { 163 path = parts[0]; 164 parts[0] = parts[0].substring(0, parts[0].indexOf(".")); 165 } 166 StructureDefinition p = getContext().getWorker().fetchResource(StructureDefinition.class, parts[0]); 167 if (p == null) { 168 p = getContext().getWorker().fetchTypeDefinition(parts[0]); 169 } 170 if (context.getTypeMap().containsKey(parts[0])) { 171 p = getContext().getWorker().fetchTypeDefinition(context.getTypeMap().get(parts[0])); 172 } 173 if (p == null) { 174 p = getContext().getWorker().fetchResource(StructureDefinition.class, link); 175 } 176 if (p != null) { 177 if ("Extension".equals(p.getType())) { 178 path = null; 179 } else if (p.hasSnapshot()) { 180 path = p.getSnapshot().getElementFirstRep().getPath(); 181 } else if (Utilities.isAbsoluteUrl(path)) { 182 path = null; 183 } 184 url = p.getWebPath(); 185 if (url == null) { 186 url = p.getUserString("filename"); 187 } 188 } else { 189 throw new DefinitionException(context.formatPhrase(RenderingContext.DATA_REND_MKDWN_LNK, link) + " "); 190 } 191 192 text = left+"["+link+"]("+url+(path == null ? "" : "#"+path)+")"+right; 193 } 194 195 // 2. markdown 196 String s = getContext().getMarkdown().process(text, "narrative generator"); 197 XhtmlParser p = new XhtmlParser(); 198 XhtmlNode m; 199 try { 200 m = p.parse("<div>"+s+"</div>", "div"); 201 } catch (org.hl7.fhir.exceptions.FHIRFormatError e) { 202 throw new FHIRFormatError(e.getMessage(), e); 203 } 204 x.getChildNodes().addAll(m.getChildNodes()); 205 } 206 } 207 208 protected void smartAddText(XhtmlNode p, String text) { 209 if (text == null) 210 return; 211 212 String[] lines = text.split("\\r\\n"); 213 for (int i = 0; i < lines.length; i++) { 214 if (i > 0) 215 p.br(); 216 p.addText(lines[i]); 217 } 218 } 219 220 // -- 3. General Purpose Terminology Support ----------------------------------------- 221 222 private static String snMonth(String m) { 223 switch (m) { 224 case "1" : return "Jan"; 225 case "2" : return "Feb"; 226 case "3" : return "Mar"; 227 case "4" : return "Apr"; 228 case "5" : return "May"; 229 case "6" : return "Jun"; 230 case "7" : return "Jul"; 231 case "8" : return "Aug"; 232 case "9" : return "Sep"; 233 case "10" : return "Oct"; 234 case "11" : return "Nov"; 235 case "12" : return "Dec"; 236 default: return null; 237 } 238 } 239 240 public static String describeVersion(String version) { 241 if (version.startsWith("http://snomed.info/sct")) { 242 String[] p = version.split("\\/"); 243 String ed = null; 244 String dt = ""; 245 246 if (p[p.length-2].equals("version")) { 247 ed = p[p.length-3]; 248 String y = p[p.length-3].substring(4, 8); 249 String m = p[p.length-3].substring(2, 4); 250 dt = " rel. "+snMonth(m)+" "+y; 251 } else { 252 ed = p[p.length-1]; 253 } 254 switch (ed) { 255 case "900000000000207008": return "Intl"+dt; 256 case "731000124108": return "US"+dt; 257 case "32506021000036107": return "AU"+dt; 258 case "449081005": return "ES"+dt; 259 case "554471000005108": return "DK"+dt; 260 case "11000146104": return "NL"+dt; 261 case "45991000052106": return "SE"+dt; 262 case "999000041000000102": return "UK"+dt; 263 case "20611000087101": return "CA"+dt; 264 case "11000172109": return "BE"+dt; 265 default: return "??"+dt; 266 } 267 } else { 268 return version; 269 } 270 } 271 272 public String displaySystem(String system) { 273 if (system == null) 274 return (context.formatPhrase(RenderingContext.DATA_REND_NOT_STAT)); 275 if (system.equals("http://loinc.org")) 276 return (context.formatPhrase(RenderingContext.DATA_REND_LOINC)); 277 if (system.startsWith("http://snomed.info")) 278 return (context.formatPhrase(RenderingContext.DATA_REND_SNOMED)); 279 if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm")) 280 return (context.formatPhrase(RenderingContext.DATA_REND_RXNORM)); 281 if (system.equals("http://hl7.org/fhir/sid/icd-9")) 282 return (context.formatPhrase(RenderingContext.DATA_REND_ICD)); 283 if (system.equals("http://dicom.nema.org/resources/ontology/DCM")) 284 return (context.formatPhrase(RenderingContext.DATA_REND_DICOM)); 285 if (system.equals("http://unitsofmeasure.org")) 286 return (context.formatPhrase(RenderingContext.GENERAL_UCUM)); 287 288 CodeSystem cs = context.getContext().fetchCodeSystem(system); 289 if (cs != null) { 290 return crPresent(cs); 291 } 292 return tails(system); 293 } 294 295 private String crPresent(CanonicalResource cr) { 296 if (cr.hasUserData("presentation")) { 297 return cr.getUserString("presentation"); 298 } 299 if (cr.hasTitle()) 300 return context.getTranslated(cr.getTitleElement()); 301 if (cr.hasName()) 302 return context.getTranslated(cr.getNameElement()); 303 return cr.toString(); 304 } 305 306 private String tails(String system) { 307 if (system.contains("/")) { 308 return system.substring(system.lastIndexOf("/")+1); 309 } else { 310 return (context.formatPhrase(RenderingContext.DATA_REND_UNKNWN)); 311 } 312 } 313 314 protected String makeAnchor(String codeSystem, String code) { 315 String s = codeSystem+'-'+code; 316 StringBuilder b = new StringBuilder(); 317 for (char c : s.toCharArray()) { 318 if (Character.isAlphabetic(c) || Character.isDigit(c) || c == '.') 319 b.append(c); 320 else 321 b.append('-'); 322 } 323 return b.toString(); 324 } 325 326 private String lookupCode(String system, String version, String code) { 327 if (JurisdictionUtilities.isJurisdiction(system)) { 328 return JurisdictionUtilities.displayJurisdiction(system+"#"+code); 329 } 330 ValidationResult t = getContext().getWorker().validateCode(getContext().getTerminologyServiceOptions().withLanguage(context.getLocale().toString().replace("_", "-")).withVersionFlexible(true), system, version, code, null); 331 332 if (t != null && t.getDisplay() != null) 333 return t.getDisplay(); 334 else 335 return code; 336 } 337 338 protected String describeLang(String lang) { 339 // special cases: 340 if ("fr-CA".equals(lang)) { 341 return "French (Canadian)"; // this one was omitted from the value set 342 } 343 ValueSet v = getContext().getWorker().findTxResource(ValueSet.class, "http://hl7.org/fhir/ValueSet/languages"); 344 if (v != null) { 345 ConceptReferenceComponent l = null; 346 for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) { 347 if (cc.getCode().equals(lang)) 348 l = cc; 349 } 350 if (l == null) { 351 if (lang.contains("-")) { 352 lang = lang.substring(0, lang.indexOf("-")); 353 } 354 for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) { 355 if (cc.getCode().equals(lang)) { 356 l = cc; 357 break; 358 } 359 } 360 if (l == null) { 361 for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) { 362 if (cc.getCode().startsWith(lang+"-")) { 363 l = cc; 364 break; 365 } 366 } 367 } 368 } 369 if (l != null) { 370 if (lang.contains("-")) 371 lang = lang.substring(0, lang.indexOf("-")); 372 String en = l.getDisplay(); 373 String nativelang = null; 374 for (ConceptReferenceDesignationComponent cd : l.getDesignation()) { 375 if (cd.getLanguage().equals(lang)) 376 nativelang = cd.getValue(); 377 } 378 if (nativelang == null) 379 return en+" ("+lang+")"; 380 else 381 return nativelang+" ("+en+", "+lang+")"; 382 } 383 } 384 return lang; 385 } 386 387 private boolean isCanonical(String path) { 388 if (!path.endsWith(".url")) 389 return false; 390 String t = path.substring(0, path.length()-4); 391 StructureDefinition sd = getContext().getWorker().fetchTypeDefinition(t); 392 if (sd == null) 393 return false; 394 if (VersionUtilities.getCanonicalResourceNames(getContext().getWorker().getVersion()).contains(t)) { 395 return true; 396 } 397 if (Utilities.existsInList(t, 398 "ActivityDefinition", "CapabilityStatement", "ChargeItemDefinition", "Citation", "CodeSystem", 399 "CompartmentDefinition", "ConceptMap", "ConditionDefinition", "EventDefinition", "Evidence", "EvidenceReport", "EvidenceVariable", 400 "ExampleScenario", "GraphDefinition", "ImplementationGuide", "Library", "Measure", "MessageDefinition", "NamingSystem", "PlanDefinition" 401 )) 402 return true; 403 return false; 404 } 405 406 // -- 4. Language support ------------------------------------------------------ 407 408 public String gt(@SuppressWarnings("rawtypes") PrimitiveType value) { 409 return context.getTranslated(value); 410 } 411 412 // -- 6. General purpose extension rendering ---------------------------------------------- 413 414 public boolean hasRenderableExtensions(DataType element) { 415 for (Extension ext : element.getExtension()) { 416 if (canRender(ext)) { 417 return true; 418 } 419 } 420 return false; 421 } 422 423 public boolean hasRenderableExtensions(BackboneType element) { 424 for (Extension ext : element.getExtension()) { 425 if (canRender(ext)) { 426 return true; 427 } 428 } 429 return element.hasModifierExtension(); 430 } 431 432 private String getExtensionLabel(Extension ext) { 433 StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, ext.getUrl()); 434 if (sd != null && ext.hasValue() && ext.getValue().isPrimitive() && sd.hasSnapshot()) { 435 for (ElementDefinition ed : sd.getSnapshot().getElement()) { 436 if (Utilities.existsInList(ed.getPath(), "Extension", "Extension.value[x]") && ed.hasLabel()) { 437 return context.getTranslated(ed.getLabelElement()); 438 } 439 } 440 } 441 return null; 442 } 443 444 private boolean canRender(Extension ext) { 445 return getExtensionLabel(ext) != null; 446 } 447 448 public void renderExtensionsInList(XhtmlNode ul, DataType element) throws FHIRFormatError, DefinitionException, IOException { 449 for (Extension ext : element.getExtension()) { 450 if (canRender(ext)) { 451 String lbl = getExtensionLabel(ext); 452 XhtmlNode li = ul.li(); 453 li.tx(lbl); 454 li.tx(": "); 455 render(li, ext.getValue()); 456 } 457 } 458 } 459 460 public void renderExtensionsInList(XhtmlNode ul, BackboneType element) throws FHIRFormatError, DefinitionException, IOException { 461 for (Extension ext : element.getModifierExtension()) { 462 if (canRender(ext)) { 463 String lbl = getExtensionLabel(ext); 464 XhtmlNode li = ul.li(); 465 li = li.b(); 466 li.tx(lbl); 467 li.tx(": "); 468 render(li, ext.getValue()); 469 } else { 470 // somehow have to do better than this 471 XhtmlNode li = ul.li(); 472 li.b().tx(context.formatPhrase(RenderingContext.DATA_REND_UNRD_EX)); 473 } 474 } 475 for (Extension ext : element.getExtension()) { 476 if (canRender(ext)) { 477 String lbl = getExtensionLabel(ext); 478 XhtmlNode li = ul.li(); 479 li.tx(lbl); 480 li.tx(": "); 481 render(li, ext.getValue()); 482 } 483 } 484 } 485 486 public void renderExtensionsInText(XhtmlNode div, DataType element, String sep) throws FHIRFormatError, DefinitionException, IOException { 487 boolean first = true; 488 for (Extension ext : element.getExtension()) { 489 if (canRender(ext)) { 490 if (first) { 491 first = false; 492 } else { 493 div.tx(sep); 494 div.tx(" "); 495 } 496 497 String lbl = getExtensionLabel(ext); 498 div.tx(lbl); 499 div.tx(": "); 500 render(div, ext.getValue()); 501 } 502 } 503 } 504 505 public void renderExtensionsInList(XhtmlNode div, BackboneType element, String sep) throws FHIRFormatError, DefinitionException, IOException { 506 boolean first = true; 507 for (Extension ext : element.getModifierExtension()) { 508 if (first) { 509 first = false; 510 } else { 511 div.tx(sep); 512 div.tx(" "); 513 } 514 if (canRender(ext)) { 515 String lbl = getExtensionLabel(ext); 516 XhtmlNode b = div.b(); 517 b.tx(lbl); 518 b.tx(": "); 519 render(div, ext.getValue()); 520 } else { 521 // somehow have to do better than this 522 div.b().tx(context.formatPhrase(RenderingContext.DATA_REND_UNRD_EX)); 523 } 524 } 525 for (Extension ext : element.getExtension()) { 526 if (canRender(ext)) { 527 if (first) { 528 first = false; 529 } else { 530 div.tx(sep); 531 div.tx(" "); 532 } 533 534 String lbl = getExtensionLabel(ext); 535 div.tx(lbl); 536 div.tx(": "); 537 render(div, ext.getValue()); 538 } 539 } 540 541 } 542 543 // -- 6. Data type Rendering ---------------------------------------------- 544 545 public static String display(IWorkerContext context, DataType type) { 546 return new DataRenderer(new RenderingContext(context, null, null, "http://hl7.org/fhir/R4", "", null, ResourceRendererMode.END_USER, GenerationRules.VALID_RESOURCE)).display(type); 547 } 548 549 public String displayBase(Base b) { 550 if (b instanceof DataType) { 551 return display((DataType) b); 552 } else { 553 return (context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, b.fhirType()) + " "); 554 } 555 } 556 557 public String display(DataType type) { 558 if (type == null || type.isEmpty()) { 559 return ""; 560 } 561 562 if (type instanceof Coding) { 563 return displayCoding((Coding) type); 564 } else if (type instanceof CodeableConcept) { 565 return displayCodeableConcept((CodeableConcept) type); 566 } else if (type instanceof Identifier) { 567 return displayIdentifier((Identifier) type); 568 } else if (type instanceof HumanName) { 569 return displayHumanName((HumanName) type); 570 } else if (type instanceof Address) { 571 return displayAddress((Address) type); 572 } else if (type instanceof ContactPoint) { 573 return displayContactPoint((ContactPoint) type); 574 } else if (type instanceof Quantity) { 575 return displayQuantity((Quantity) type); 576 } else if (type instanceof Range) { 577 return displayRange((Range) type); 578 } else if (type instanceof Period) { 579 return displayPeriod((Period) type); 580 } else if (type instanceof Timing) { 581 return displayTiming((Timing) type); 582 } else if (type instanceof SampledData) { 583 return displaySampledData((SampledData) type); 584 } else if (type instanceof ContactDetail) { 585 return displayContactDetail((ContactDetail) type); 586 } else if (type.isDateTime()) { 587 return displayDateTime((BaseDateTimeType) type); 588 } else if (type.isPrimitive()) { 589 return context.getTranslated((PrimitiveType<?>) type); 590 } else { 591 return (context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, type.fhirType()) + " "); 592 } 593 } 594 595 protected String displayDateTime(BaseDateTimeType type) { 596 if (!type.hasPrimitiveValue()) { 597 return ""; 598 } 599 600 // relevant inputs in rendering context: 601 // timeZone, dateTimeFormat, locale, mode 602 // timezone - application specified timezone to use. 603 // null = default to the time of the date/time itself 604 // dateTimeFormat - application specified format for date times 605 // null = default to ... depends on mode 606 // mode - if rendering mode is technical, format defaults to XML format 607 // locale - otherwise, format defaults to SHORT for the Locale (which defaults to default Locale) 608 if (isOnlyDate(type.getPrecision())) { 609 610 DateTimeFormatter fmt = getDateFormatForPrecision(type); 611 LocalDate date = LocalDate.of(type.getYear(), type.getMonth()+1, type.getDay()); 612 return fmt.format(date); 613 } 614 615 DateTimeFormatter fmt = context.getDateTimeFormat(); 616 if (fmt == null) { 617 if (context.isTechnicalMode()) { 618 fmt = DateTimeFormatter.ISO_OFFSET_DATE_TIME; 619 } else { 620 fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withLocale(context.getLocale()); 621 } 622 } 623 ZonedDateTime zdt = ZonedDateTime.parse(type.primitiveValue()); 624 ZoneId zone = context.getTimeZoneId(); 625 if (zone != null) { 626 zdt = zdt.withZoneSameInstant(zone); 627 } 628 return fmt.format(zdt); 629 } 630 631 private DateTimeFormatter getDateFormatForPrecision(BaseDateTimeType type) { 632 DateTimeFormatter fmt = getContextDateFormat(type); 633 if (fmt != null) { 634 return fmt; 635 } 636 if (context.isTechnicalMode()) { 637 switch (type.getPrecision()) { 638 case YEAR: 639 return new DateTimeFormatterBuilder().appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD).toFormatter(); 640 case MONTH: 641 return new DateTimeFormatterBuilder().appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD).appendLiteral('-').appendValue(MONTH_OF_YEAR, 2).toFormatter(); 642 default: 643 return DateTimeFormatter.ISO_DATE; 644 } 645 } else { 646 switch (type.getPrecision()) { 647 case YEAR: 648 return DateTimeFormatter.ofPattern("uuuu"); 649 case MONTH: 650 return DateTimeFormatter.ofPattern("MMM uuuu"); 651 default: 652 return DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(context.getLocale()); 653 } 654 } 655 } 656 657 private DateTimeFormatter getContextDateFormat(BaseDateTimeType type) { 658 switch (type.getPrecision()) { 659 case YEAR: 660 return context.getDateYearFormat(); 661 case MONTH: 662 return context.getDateYearMonthFormat(); 663 default: 664 return context.getDateFormat(); 665 } 666 } 667 668 private boolean isOnlyDate(TemporalPrecisionEnum temporalPrecisionEnum) { 669 return temporalPrecisionEnum == TemporalPrecisionEnum.YEAR || temporalPrecisionEnum == TemporalPrecisionEnum.MONTH || temporalPrecisionEnum == TemporalPrecisionEnum.DAY; 670 } 671 672 public String display(BaseWrapper type) { 673 return (context.formatPhrase(RenderingContext.DATA_REND_TO_DO)); 674 } 675 676 public void render(XhtmlNode x, BaseWrapper type) throws FHIRFormatError, DefinitionException, IOException { 677 Base base = null; 678 try { 679 base = type.getBase(); 680 } catch (FHIRException | IOException e) { 681 x.tx(context.formatPhrase(RenderingContext.DATA_REND_ERROR, e.getMessage()) + " "); // this shouldn't happen - it's an error in the library itself 682 return; 683 } 684 if (base instanceof DataType) { 685 render(x, (DataType) base); 686 } else { 687 x.tx(context.formatPhrase(RenderingContext.DATA_REND_TO_DO, base.fhirType())); 688 } 689 } 690 691 public void renderBase(XhtmlNode x, Base b) throws FHIRFormatError, DefinitionException, IOException { 692 if (b instanceof DataType) { 693 render(x, (DataType) b); 694 } else { 695 x.tx(context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, b.fhirType()) + " "); 696 } 697 } 698 699 public void render(XhtmlNode x, DataType type) throws FHIRFormatError, DefinitionException, IOException { 700 if (type instanceof BaseDateTimeType) { 701 x.tx(displayDateTime((BaseDateTimeType) type)); 702 } else if (type instanceof UriType) { 703 renderUri(x, (UriType) type); 704 } else if (type instanceof Annotation) { 705 renderAnnotation(x, (Annotation) type); 706 } else if (type instanceof Coding) { 707 renderCodingWithDetails(x, (Coding) type); 708 } else if (type instanceof CodeableConcept) { 709 renderCodeableConcept(x, (CodeableConcept) type); 710 } else if (type instanceof Identifier) { 711 renderIdentifier(x, (Identifier) type); 712 } else if (type instanceof HumanName) { 713 renderHumanName(x, (HumanName) type); 714 } else if (type instanceof Address) { 715 renderAddress(x, (Address) type); 716 } else if (type instanceof Expression) { 717 renderExpression(x, (Expression) type); 718 } else if (type instanceof Money) { 719 renderMoney(x, (Money) type); 720 } else if (type instanceof ContactPoint) { 721 renderContactPoint(x, (ContactPoint) type); 722 } else if (type instanceof Quantity) { 723 renderQuantity(x, (Quantity) type); 724 } else if (type instanceof Range) { 725 renderRange(x, (Range) type); 726 } else if (type instanceof Period) { 727 renderPeriod(x, (Period) type); 728 } else if (type instanceof Timing) { 729 renderTiming(x, (Timing) type); 730 } else if (type instanceof SampledData) { 731 renderSampledData(x, (SampledData) type); 732 } else if (type instanceof Reference) { 733 renderReference(x, (Reference) type); 734 } else if (type instanceof UsageContext) { 735 renderUsageContext(x, (UsageContext) type); 736 } else if (type instanceof CodeableReference) { 737 CodeableReference cr = (CodeableReference) type; 738 if (cr.hasConcept()) { 739 renderCodeableConcept(x, cr.getConcept()); 740 } else { 741 renderReference(x, cr.getReference()); 742 } 743 } else if (type instanceof MarkdownType) { 744 addMarkdown(x, context.getTranslated((MarkdownType) type)); 745 } else if (type instanceof Base64BinaryType) { 746 Base64BinaryType b64 = (Base64BinaryType) type; 747 x.tx(context.formatPhrase(RenderingContext.DATA_REND_BASE64, (b64.getValue() == null ? "0" : b64.getValue().length))); 748 } else if (type.isPrimitive()) { 749 x.tx(context.getTranslated((PrimitiveType<?>) type)); 750 } else { 751 x.tx(context.formatPhrase(RenderingContext.DATA_REND_NO_DISP, type.fhirType()) + " "); 752 } 753 } 754 755 protected void renderReference(XhtmlNode x, Reference ref) { 756 if (ref.hasDisplay()) { 757 x.tx(context.getTranslated(ref.getDisplayElement())); 758 } else if (ref.hasReference()) { 759 x.tx(ref.getReference()); 760 } else { 761 x.tx("??"); 762 } 763 } 764 765 public void renderDateTime(XhtmlNode x, Base e) { 766 if (e.hasPrimitiveValue()) { 767 x.addText(displayDateTime((DateTimeType) e)); 768 } 769 } 770 771 public void renderDate(XhtmlNode x, Base e) { 772 if (e.hasPrimitiveValue()) { 773 x.addText(displayDateTime((DateType) e)); 774 } 775 } 776 777 public void renderDateTime(XhtmlNode x, String s) { 778 if (s != null) { 779 DateTimeType dt = new DateTimeType(s); 780 x.addText(displayDateTime(dt)); 781 } 782 } 783 784 protected void renderUri(XhtmlNode x, UriType uri) { 785 if (uri.getValue().startsWith("mailto:")) { 786 x.ah(uri.getValue()).addText(uri.getValue().substring(7)); 787 } else { 788 Resource r = context.getContext().fetchResource(Resource.class, uri.getValue()); 789 if (r != null && r.getWebPath() != null) { 790 if (r instanceof CanonicalResource) { 791 x.ah(r.getWebPath()).addText(crPresent((CanonicalResource) r)); 792 } else { 793 x.ah(r.getWebPath()).addText(uri.getValue()); 794 } 795 } else { 796 String url = context.getResolver() != null ? context.getResolver().resolveUri(context, uri.getValue()) : null; 797 if (url != null) { 798 x.ah(url).addText(uri.getValue()); 799 } else if (Utilities.isAbsoluteUrlLinkable(uri.getValue()) && !(uri instanceof IdType)) { 800 x.ah(uri.getValue()).addText(uri.getValue()); 801 } else { 802 x.addText(uri.getValue()); 803 } 804 } 805 } 806 } 807 808 protected void renderUri(XhtmlNode x, UriType uriD, String path, String id, Resource src) { 809 String uri = uriD.getValue(); 810 if (isCanonical(path)) { 811 x.code().tx(uri); 812 } else { 813 if (uri == null) { 814 x.b().tx(uri); 815 } else if (uri.startsWith("mailto:")) { 816 x.ah(uri).addText(uri.substring(7)); 817 } else { 818 Resource target = context.getContext().fetchResource(Resource.class, uri, src); 819 if (target != null && target.hasWebPath()) { 820 String title = target instanceof CanonicalResource ? crPresent((CanonicalResource) target) : uri; 821 x.ah(target.getWebPath()).addText(title); 822 } else { 823 String url = context.getResolver() != null ? context.getResolver().resolveUri(context, uri) : null; 824 if (url != null) { 825 x.ah(url).addText(uri); 826 } else if (uri.contains("|")) { 827 x.ah(uri.substring(0, uri.indexOf("|"))).addText(uri); 828 } else if (uri.startsWith("http:") || uri.startsWith("https:") || uri.startsWith("ftp:")) { 829 x.ah(uri).addText(uri); 830 } else { 831 x.code().addText(uri); 832 } 833 } 834 } 835 } 836 } 837 838 protected void renderAnnotation(XhtmlNode x, Annotation annot) { 839 renderAnnotation(x, annot, false); 840 } 841 842 protected void renderAnnotation(XhtmlNode x, Annotation a, boolean showCodeDetails) throws FHIRException { 843 StringBuilder b = new StringBuilder(); 844 if (a.hasText()) { 845 b.append(context.getTranslated(a.getTextElement())); 846 } 847 848 if (a.hasText() && (a.hasAuthor() || a.hasTimeElement())) { 849 b.append(" ("); 850 } 851 852 if (a.hasAuthor()) { 853 b.append(context.formatPhrase(RenderingContext.DATA_REND_BY) + " "); 854 if (a.hasAuthorReference()) { 855 b.append(a.getAuthorReference().getReference()); 856 } else if (a.hasAuthorStringType()) { 857 b.append(context.getTranslated(a.getAuthorStringType())); 858 } 859 } 860 861 862 if (a.hasTimeElement()) { 863 if (b.length() > 0) { 864 b.append(" "); 865 } 866 b.append("@").append(a.getTimeElement().toHumanDisplay()); 867 } 868 if (a.hasText() && (a.hasAuthor() || a.hasTimeElement())) { 869 b.append(")"); 870 } 871 872 873 x.addText(b.toString()); 874 } 875 876 public String displayCoding(Coding c) { 877 String s = ""; 878 if (context.isTechnicalMode()) { 879 s = context.getTranslated(c.getDisplayElement()); 880 if (Utilities.noString(s)) { 881 s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); 882 } 883 if (Utilities.noString(s)) { 884 s = displayCodeTriple(c.getSystem(), c.getVersion(), c.getCode()); 885 } else if (c.hasSystem()) { 886 s = s + " ("+displayCodeTriple(c.getSystem(), c.getVersion(), c.getCode())+")"; 887 } else if (c.hasCode()) { 888 s = s + " ("+c.getCode()+")"; 889 } 890 } else { 891 if (c.hasDisplayElement()) 892 return context.getTranslated(c.getDisplayElement()); 893 if (Utilities.noString(s)) 894 s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); 895 if (Utilities.noString(s)) 896 s = c.getCode(); 897 } 898 return s; 899 } 900 901 private String displayCodeSource(String system, String version) { 902 String s = displaySystem(system); 903 if (version != null) { 904 s = s + "["+describeVersion(version)+"]"; 905 } 906 return s; 907 } 908 909 private String displayCodeTriple(String system, String version, String code) { 910 if (system == null) { 911 if (code == null) { 912 return ""; 913 } else { 914 return "#"+code; 915 } 916 } else { 917 String s = displayCodeSource(system, version); 918 if (code != null) { 919 s = s + "#"+code; 920 } 921 return s; 922 } 923 } 924 925 public String displayCoding(List<Coding> list) { 926 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 927 for (Coding c : list) { 928 b.append(displayCoding(c)); 929 } 930 return b.toString(); 931 } 932 933 protected void renderCoding(XhtmlNode x, Coding c) { 934 renderCoding(x, c, false); 935 } 936 937 protected void renderCoding(HierarchicalTableGenerator gen, List<Piece> pieces, Coding c) { 938 if (c.isEmpty()) { 939 return; 940 } 941 942 String url = getLinkForSystem(c.getSystem(), c.getVersion()); 943 String name = displayCodeSource(c.getSystem(), c.getVersion()); 944 if (!Utilities.noString(url)) { 945 pieces.add(gen.new Piece(url, name, c.getSystem()+(c.hasVersion() ? "#"+c.getVersion() : ""))); 946 } else { 947 pieces.add(gen.new Piece(null, name, c.getSystem()+(c.hasVersion() ? "#"+c.getVersion() : ""))); 948 } 949 pieces.add(gen.new Piece(null, "#"+c.getCode(), null)); 950 String s = context.getTranslated(c.getDisplayElement()); 951 if (Utilities.noString(s)) { 952 s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); 953 } 954 if (!Utilities.noString(s)) { 955 pieces.add(gen.new Piece(null, " \""+s+"\"", null)); 956 } 957 } 958 959 private String getLinkForSystem(String system, String version) { 960 if ("http://snomed.info/sct".equals(system)) { 961 return "https://browser.ihtsdotools.org/"; 962 } else if ("http://loinc.org".equals(system)) { 963 return "https://loinc.org/"; 964 } else if ("http://unitsofmeasure.org".equals(system)) { 965 return "http://ucum.org"; 966 } else { 967 String url = system; 968 if (version != null) { 969 url = url + "|"+version; 970 } 971 CodeSystem cs = context.getWorker().fetchCodeSystem(url); 972 if (cs != null && cs.hasWebPath()) { 973 return cs.getWebPath(); 974 } 975 return null; 976 } 977 } 978 979 protected String getLinkForCode(String system, String version, String code) { 980 if ("http://snomed.info/sct".equals(system)) { 981 if (!Utilities.noString(code)) { 982 return "http://snomed.info/id/"+code; 983 } else { 984 return "https://browser.ihtsdotools.org/"; 985 } 986 } else if ("http://loinc.org".equals(system)) { 987 if (!Utilities.noString(code)) { 988 return "https://loinc.org/"+code; 989 } else { 990 return "https://loinc.org/"; 991 } 992 } else if ("http://www.nlm.nih.gov/research/umls/rxnorm".equals(system)) { 993 if (!Utilities.noString(code)) { 994 return "https://mor.nlm.nih.gov/RxNav/search?searchBy=RXCUI&searchTerm="+code; 995 } else { 996 return "https://www.nlm.nih.gov/research/umls/rxnorm/index.html"; 997 } 998 } else if ("urn:iso:std:iso:3166".equals(system)) { 999 if (!Utilities.noString(code)) { 1000 return "https://en.wikipedia.org/wiki/ISO_3166-2:"+code; 1001 } else { 1002 return "https://en.wikipedia.org/wiki/ISO_3166-2"; 1003 } 1004 } else { 1005 CodeSystem cs = context.getWorker().fetchCodeSystem(system, version); 1006 if (cs != null && cs.hasWebPath()) { 1007 if (!Utilities.noString(code)) { 1008 return cs.getWebPath()+"#"+cs.getId()+"-"+Utilities.nmtokenize(code); 1009 } else { 1010 return cs.getWebPath(); 1011 } 1012 } 1013 } 1014 return null; 1015 } 1016 1017 public CodeResolution resolveCode(String system, String code) { 1018 return resolveCode(new Coding().setSystem(system).setCode(code)); 1019 } 1020 1021 public CodeResolution resolveCode(Coding c) { 1022 String systemName; 1023 String systemLink; 1024 String link; 1025 String display = null; 1026 String hint; 1027 1028 if (c.hasDisplayElement()) 1029 display = context.getTranslated(c.getDisplayElement()); 1030 if (Utilities.noString(display)) 1031 display = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); 1032 if (Utilities.noString(display)) { 1033 display = c.getCode(); 1034 } 1035 1036 CodeSystem cs = context.getWorker().fetchCodeSystem(c.getSystem()); 1037 systemLink = cs != null ? cs.getWebPath() : null; 1038 systemName = cs != null ? crPresent(cs) : displaySystem(c.getSystem()); 1039 link = getLinkForCode(c.getSystem(), c.getVersion(), c.getCode()); 1040 1041 hint = systemName+": "+display+(c.hasVersion() ? " "+ context.formatPhrase(RenderingContext.DATA_REND_VERSION, c.getVersion(), ")") : ""); 1042 return new CodeResolution(systemName, systemLink, link, display, hint); 1043 } 1044 1045 public CodeResolution resolveCode(CodeableConcept code) { 1046 if (code.hasCoding()) { 1047 return resolveCode(code.getCodingFirstRep()); 1048 } else { 1049 return new CodeResolution(null, null, null, code.getText(), code.getText()); 1050 } 1051 } 1052 protected void renderCodingWithDetails(XhtmlNode x, Coding c) { 1053 String s = ""; 1054 if (c.hasDisplayElement()) 1055 s = context.getTranslated(c.getDisplayElement()); 1056 if (Utilities.noString(s)) 1057 s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); 1058 1059 CodeSystem cs = context.getWorker().fetchCodeSystem(c.getSystem()); 1060 1061 String sn = cs != null ? crPresent(cs) : displaySystem(c.getSystem()); 1062 String link = getLinkForCode(c.getSystem(), c.getVersion(), c.getCode()); 1063 if (link != null) { 1064 x.ah(link).tx(sn); 1065 } else { 1066 x.tx(sn); 1067 } 1068 1069 x.tx(" "); 1070 x.tx(c.getCode()); 1071 if (!Utilities.noString(s)) { 1072 x.tx(": "); 1073 x.tx(s); 1074 } 1075 if (c.hasVersion()) { 1076 x.tx(" "+context.formatPhrase(RenderingContext.DATA_REND_VERSION, c.getVersion(), ")")); 1077 } 1078 } 1079 1080 protected void renderCoding(XhtmlNode x, Coding c, boolean showCodeDetails) { 1081 String s = ""; 1082 if (c.hasDisplayElement()) 1083 s = context.getTranslated(c.getDisplayElement()); 1084 if (Utilities.noString(s)) 1085 s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); 1086 1087 if (Utilities.noString(s)) 1088 s = c.getCode(); 1089 1090 if (showCodeDetails) { 1091 x.addText(s+" "+context.formatPhrase(RenderingContext.DATA_REND_DETAILS_STATED, displaySystem(c.getSystem()), c.getCode(), " = '", lookupCode(c.getSystem(), c.getVersion(), c.getCode()), c.getDisplay(), "')")); 1092 } else 1093 x.span(null, "{"+c.getSystem()+" "+c.getCode()+"}").addText(s); 1094 } 1095 1096 public String displayCodeableConcept(CodeableConcept cc) { 1097 String s = context.getTranslated(cc.getTextElement()); 1098 if (Utilities.noString(s)) { 1099 for (Coding c : cc.getCoding()) { 1100 if (c.hasDisplayElement()) { 1101 s = context.getTranslated(c.getDisplayElement()); 1102 break; 1103 } 1104 } 1105 } 1106 if (Utilities.noString(s)) { 1107 // still? ok, let's try looking it up 1108 for (Coding c : cc.getCoding()) { 1109 if (c.hasCode() && c.hasSystem()) { 1110 s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); 1111 if (!Utilities.noString(s)) 1112 break; 1113 } 1114 } 1115 } 1116 1117 if (Utilities.noString(s)) { 1118 if (cc.getCoding().isEmpty()) 1119 s = ""; 1120 else 1121 s = cc.getCoding().get(0).getCode(); 1122 } 1123 return s; 1124 } 1125 1126 protected void renderCodeableConcept(XhtmlNode x, CodeableConcept cc) throws FHIRFormatError, DefinitionException, IOException { 1127 renderCodeableConcept(x, cc, false); 1128 } 1129 1130 protected void renderCodeableReference(XhtmlNode x, CodeableReference e, boolean showCodeDetails) throws FHIRFormatError, DefinitionException, IOException { 1131 if (e.hasConcept()) { 1132 renderCodeableConcept(x, e.getConcept(), showCodeDetails); 1133 } 1134 if (e.hasReference()) { 1135 renderReference(x, e.getReference()); 1136 } 1137 } 1138 1139 protected void renderCodeableConcept(XhtmlNode x, CodeableConcept cc, boolean showCodeDetails) throws FHIRFormatError, DefinitionException, IOException { 1140 if (cc.isEmpty()) { 1141 return; 1142 } 1143 1144 String s = context.getTranslated(cc.getTextElement()); 1145 if (Utilities.noString(s)) { 1146 for (Coding c : cc.getCoding()) { 1147 if (c.hasDisplayElement()) { 1148 s = context.getTranslated(c.getDisplayElement()); 1149 break; 1150 } 1151 } 1152 } 1153 if (Utilities.noString(s)) { 1154 // still? ok, let's try looking it up 1155 for (Coding c : cc.getCoding()) { 1156 if (c.hasCodeElement() && c.hasSystemElement()) { 1157 s = lookupCode(c.getSystem(), c.getVersion(), c.getCode()); 1158 if (!Utilities.noString(s)) 1159 break; 1160 } 1161 } 1162 } 1163 1164 if (Utilities.noString(s)) { 1165 if (cc.getCoding().isEmpty()) 1166 s = ""; 1167 else 1168 s = cc.getCoding().get(0).getCode(); 1169 } 1170 1171 if (showCodeDetails) { 1172 x.addText(s+" "); 1173 XhtmlNode sp = x.span("background: LightGoldenRodYellow; margin: 4px; border: 1px solid khaki", null); 1174 sp.tx(" ("); 1175 boolean first = true; 1176 for (Coding c : cc.getCoding()) { 1177 if (first) { 1178 first = false; 1179 } else { 1180 sp.tx("; "); 1181 } 1182 String url = getLinkForSystem(c.getSystem(), c.getVersion()); 1183 if (url != null) { 1184 sp.ah(url).tx(displayCodeSource(c.getSystem(), c.getVersion())); 1185 } else { 1186 sp.tx(displayCodeSource(c.getSystem(), c.getVersion())); 1187 } 1188 if (c.hasCode()) { 1189 sp.tx("#"+c.getCode()); 1190 } 1191 if (c.hasDisplay() && !s.equals(c.getDisplay())) { 1192 sp.tx(" \""+context.getTranslated(c.getDisplayElement())+"\""); 1193 } 1194 } 1195 if (hasRenderableExtensions(cc)) { 1196 if (!first) { 1197 sp.tx("; "); 1198 } 1199 renderExtensionsInText(sp, cc, ";"); 1200 } 1201 sp.tx(")"); 1202 } else { 1203 1204 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 1205 for (Coding c : cc.getCoding()) { 1206 if (c.hasCodeElement() && c.hasSystemElement()) { 1207 b.append("{"+c.getSystem()+" "+c.getCode()+"}"); 1208 } 1209 } 1210 1211 x.span(null, context.formatPhrase(RenderingContext.DATA_REND_CODES) +b.toString()).addText(s); 1212 } 1213 } 1214 1215 protected String displayIdentifier(Identifier ii) { 1216 String s = Utilities.noString(ii.getValue()) ? "?ngen-9?" : ii.getValue(); 1217 if ("urn:ietf:rfc:3986".equals(ii.getSystem()) && s.startsWith("urn:oid:")) { 1218 s = "OID:"+s.substring(8); 1219 } else if ("urn:ietf:rfc:3986".equals(ii.getSystem()) && s.startsWith("urn:uuid:")) { 1220 s = "UUID:"+s.substring(9); 1221 } else { 1222 NamingSystem ns = context.getContext().getNSUrlMap().get(ii.getSystem()); 1223 if (ns != null) { 1224 s = crPresent(ns)+"#"+s; 1225 } 1226 if (ii.hasType()) { 1227 if (ii.getType().hasText()) 1228 s = context.getTranslated(ii.getType().getTextElement())+":\u00A0"+s; 1229 else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasDisplay()) 1230 s = context.getTranslated(ii.getType().getCoding().get(0).getDisplayElement())+": "+s; 1231 else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasCode()) 1232 s = lookupCode(ii.getType().getCoding().get(0).getSystem(), ii.getType().getCoding().get(0).getVersion(), ii.getType().getCoding().get(0).getCode())+": "+s; 1233 } else if (ii.hasSystem()) { 1234 s = ii.getSystem()+"#"+s; 1235 } 1236 } 1237 1238 if (ii.hasUse() || ii.hasPeriod()) { 1239 s = s + "\u00A0("; 1240 if (ii.hasUse()) { 1241 s = s + "use:\u00A0"+ii.getUse().toCode(); 1242 } 1243 if (ii.hasUse() && ii.hasPeriod()) { 1244 s = s + ",\u00A0"; 1245 } 1246 if (ii.hasPeriod()) { 1247 s = s + "period:\u00A0"+displayPeriod(ii.getPeriod()); 1248 } 1249 s = s + ")"; 1250 } 1251 return s; 1252 } 1253 1254 protected void renderIdentifier(XhtmlNode x, Identifier ii) { 1255 if (ii.hasType()) { 1256 if (ii.getType().hasText()) { 1257 x.tx(context.getTranslated(ii.getType().getTextElement())); 1258 } else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasDisplay()) { 1259 x.tx(context.getTranslated(ii.getType().getCoding().get(0).getDisplayElement())); 1260 } else if (ii.getType().hasCoding() && ii.getType().getCoding().get(0).hasCode()) { 1261 x.tx(lookupCode(ii.getType().getCoding().get(0).getSystem(), ii.getType().getCoding().get(0).getVersion(), ii.getType().getCoding().get(0).getCode())); 1262 } 1263 x.tx("/"); 1264 } else if (ii.hasSystem()) { 1265 NamingSystem ns = context.getContext().getNSUrlMap().get(ii.getSystem()); 1266 if (ns != null) { 1267 if (ns.hasWebPath()) { 1268 x.ah(ns.getWebPath(), ns.getDescription()).tx(crPresent(ns)); 1269 } else { 1270 x.tx(crPresent(ns)); 1271 } 1272 } else { 1273 switch (ii.getSystem()) { 1274 case "urn:oid:2.51.1.3": 1275 x.ah("https://www.gs1.org/standards/id-keys/gln", context.formatPhrase(RenderingContext.DATA_REND_GLN)).tx("GLN"); 1276 break; 1277 default: 1278 x.code(ii.getSystem()); 1279 } 1280 } 1281 x.tx("/"); 1282 } 1283 x.tx(Utilities.noString(ii.getValue()) ? "?ngen-9?" : ii.getValue()); 1284 1285 if (ii.hasUse() || ii.hasPeriod()) { 1286 x.nbsp(); 1287 x.tx("("); 1288 if (ii.hasUse()) { 1289 x.tx(context.formatPhrase(RenderingContext.DATA_REND_USE)); 1290 x.nbsp(); 1291 x.tx(ii.getUse().toCode()); 1292 } 1293 if (ii.hasUse() && ii.hasPeriod()) { 1294 x.tx(","); 1295 x.nbsp(); 1296 } 1297 if (ii.hasPeriod()) { 1298 x.tx(context.formatPhrase(RenderingContext.DATA_REND_PERIOD)); 1299 x.nbsp(); 1300 x.tx(displayPeriod(ii.getPeriod())); 1301 } 1302 x.tx(")"); 1303 } 1304 } 1305 1306 public static String displayHumanName(HumanName name) { 1307 StringBuilder s = new StringBuilder(); 1308 if (name.hasText()) 1309 s.append(name.getText()); 1310 else { 1311 for (StringType p : name.getGiven()) { 1312 s.append(p.getValue()); 1313 s.append(" "); 1314 } 1315 if (name.hasFamily()) { 1316 s.append(name.getFamily()); 1317 s.append(" "); 1318 } 1319 } 1320 if (name.hasUse() && name.getUse() != NameUse.USUAL) 1321 s.append("("+name.getUse().toString()+")"); 1322 return s.toString(); 1323 } 1324 1325 1326 protected void renderHumanName(XhtmlNode x, HumanName name) { 1327 StringBuilder s = new StringBuilder(); 1328 if (name.hasText()) 1329 s.append(context.getTranslated(name.getTextElement())); 1330 else { 1331 for (StringType p : name.getGiven()) { 1332 s.append(context.getTranslated(p)); 1333 s.append(" "); 1334 } 1335 if (name.hasFamily()) { 1336 s.append(context.getTranslated(name.getFamilyElement())); 1337 s.append(" "); 1338 } 1339 } 1340 if (name.hasUse() && name.getUse() != NameUse.USUAL) 1341 s.append("("+context.getTranslatedCode(name.getUseElement(), "http://hl7.org/fhir/name-use")+")"); 1342 1343 x.addText(s.toString()); 1344 } 1345 1346 private String displayAddress(Address address) { 1347 StringBuilder s = new StringBuilder(); 1348 if (address.hasText()) 1349 s.append(context.getTranslated(address.getTextElement())); 1350 else { 1351 for (StringType p : address.getLine()) { 1352 s.append(context.getTranslated(p)); 1353 s.append(" "); 1354 } 1355 if (address.hasCity()) { 1356 s.append(context.getTranslated(address.getCityElement())); 1357 s.append(" "); 1358 } 1359 if (address.hasState()) { 1360 s.append(context.getTranslated(address.getStateElement())); 1361 s.append(" "); 1362 } 1363 1364 if (address.hasPostalCode()) { 1365 s.append(context.getTranslated(address.getPostalCodeElement())); 1366 s.append(" "); 1367 } 1368 1369 if (address.hasCountry()) { 1370 s.append(context.getTranslated(address.getCountryElement())); 1371 s.append(" "); 1372 } 1373 } 1374 if (address.hasUse()) 1375 s.append("("+address.getUse().toCode()+")"); 1376 return s.toString(); 1377 } 1378 1379 protected void renderAddress(XhtmlNode x, Address address) { 1380 x.addText(displayAddress(address)); 1381 } 1382 1383 1384 public static String displayContactPoint(ContactPoint contact) { 1385 StringBuilder s = new StringBuilder(); 1386 s.append(describeSystem(contact.getSystem())); 1387 if (Utilities.noString(contact.getValue())) 1388 s.append("-unknown-"); 1389 else 1390 s.append(contact.getValue()); 1391 if (contact.hasUse()) 1392 s.append("("+contact.getUse().toString()+")"); 1393 return s.toString(); 1394 } 1395 1396 public static String displayContactDetail(ContactDetail contact) { 1397 CommaSeparatedStringBuilder s = new CommaSeparatedStringBuilder(); 1398 for (ContactPoint cp : contact.getTelecom()) { 1399 s.append(displayContactPoint(cp)); 1400 } 1401 return contact.getName()+(s.length() == 0 ? "" : " ("+s.toString()+")"); 1402 } 1403 1404 protected String getLocalizedBigDecimalValue(BigDecimal input, Currency c) { 1405 NumberFormat numberFormat = NumberFormat.getNumberInstance(context.getLocale()); 1406 numberFormat.setGroupingUsed(true); 1407 numberFormat.setMaximumFractionDigits(c.getDefaultFractionDigits()); 1408 numberFormat.setMinimumFractionDigits(c.getDefaultFractionDigits()); 1409 return numberFormat.format(input); 1410} 1411 1412 protected void renderMoney(XhtmlNode x, Money money) { 1413 if (x.getName().equals("blockquote")) { 1414 x = x.para(); 1415 } 1416 Currency c = money.hasCurrency() ? Currency.getInstance(money.getCurrency()) : null; 1417 if (c != null) { 1418 XhtmlNode s = x.span(null, c.getDisplayName()); 1419 s.tx(c.getSymbol(context.getLocale())); 1420 s.tx(getLocalizedBigDecimalValue(money.getValue(), c)); 1421 x.tx(" ("+c.getCurrencyCode()+")"); 1422 } else { 1423 if (money.getCurrency() != null) { 1424 x.tx(money.getCurrency()); 1425 } 1426 x.tx(money.getValue().toPlainString()); 1427 } 1428 } 1429 1430 protected void renderExpression(XhtmlNode x, Expression expr) { 1431 // there's two parts: what the expression is, and how it's described. 1432 // we start with what it is, and then how it's described 1433 XhtmlNode p = x; 1434 if (p.getName().equals("blockquote")) { 1435 p = p.para(); 1436 } 1437 if (expr.hasExpression()) { 1438 if (expr.hasReference()) { 1439 p = x.ah(expr.getReference()); 1440 } 1441 XhtmlNode c = p; 1442 if (expr.hasLanguage()) { 1443 c = c.span(null, expr.getLanguage()); 1444 } 1445 c.code().tx(expr.getExpression()); 1446 } else if (expr.hasReference()) { 1447 p.ah(expr.getReference()).tx(context.formatPhrase(RenderingContext.DATA_REND_SOURCE)); 1448 } 1449 if (expr.hasName() || expr.hasDescription()) { 1450 p.tx("("); 1451 if (expr.hasName()) { 1452 p.b().tx(expr.getName()); 1453 } 1454 if (expr.hasDescription()) { 1455 p.tx("\""); 1456 p.tx(context.getTranslated(expr.getDescriptionElement())); 1457 p.tx("\""); 1458 } 1459 p.tx(")"); 1460 } 1461 } 1462 1463 1464 protected void renderContactPoint(XhtmlNode x, ContactPoint contact) { 1465 if (contact != null) { 1466 if (!contact.hasSystem()) { 1467 x.addText(displayContactPoint(contact)); 1468 } else { 1469 switch (contact.getSystem()) { 1470 case EMAIL: 1471 x.ah("mailto:"+contact.getValue()).tx(contact.getValue()); 1472 break; 1473 case FAX: 1474 x.addText(displayContactPoint(contact)); 1475 break; 1476 case NULL: 1477 x.addText(displayContactPoint(contact)); 1478 break; 1479 case OTHER: 1480 x.addText(displayContactPoint(contact)); 1481 break; 1482 case PAGER: 1483 x.addText(displayContactPoint(contact)); 1484 break; 1485 case PHONE: 1486 if (contact.hasValue() && contact.getValue() != null && contact.getValue().startsWith("+")) { 1487 x.ah("tel:"+contact.getValue().replace(" ", "")).tx(contact.getValue()); 1488 } else { 1489 x.addText(displayContactPoint(contact)); 1490 } 1491 break; 1492 case SMS: 1493 x.addText(displayContactPoint(contact)); 1494 break; 1495 case URL: 1496 x.ah(contact.getValue()).tx(contact.getValue()); 1497 break; 1498 default: 1499 break; 1500 } 1501 } 1502 } 1503 } 1504 1505 protected void displayContactPoint(XhtmlNode p, ContactPoint c) { 1506 if (c != null) { 1507 if (c.getSystem() == ContactPointSystem.PHONE) { 1508 p.tx(context.formatPhrase(RenderingContext.DATA_REND_PHONE, c.getValue()) + " "); 1509 } else if (c.getSystem() == ContactPointSystem.FAX) { 1510 p.tx(context.formatPhrase(RenderingContext.DATA_REND_FAX, c.getValue()) + " "); 1511 } else if (c.getSystem() == ContactPointSystem.EMAIL) { 1512 p.tx(c.getValue()); 1513 } else if (c.getSystem() == ContactPointSystem.URL) { 1514 if (c.getValue().length() > 30) { 1515 p.addText(c.getValue().substring(0, 30)+"..."); 1516 } else { 1517 p.addText(c.getValue()); 1518 } 1519 } 1520 } 1521 } 1522 1523 protected void addTelecom(XhtmlNode p, ContactPoint c) { 1524 if (c.getSystem() == ContactPointSystem.PHONE) { 1525 p.tx(context.formatPhrase(RenderingContext.DATA_REND_PHONE, c.getValue()) + " "); 1526 } else if (c.getSystem() == ContactPointSystem.FAX) { 1527 p.tx(context.formatPhrase(RenderingContext.DATA_REND_FAX, c.getValue()) + " "); 1528 } else if (c.getSystem() == ContactPointSystem.EMAIL) { 1529 p.ah("mailto:"+c.getValue()).addText(c.getValue()); 1530 } else if (c.getSystem() == ContactPointSystem.URL) { 1531 if (c.getValue().length() > 30) 1532 p.ah(c.getValue()).addText(c.getValue().substring(0, 30)+"..."); 1533 else 1534 p.ah(c.getValue()).addText(c.getValue()); 1535 } 1536 } 1537 private static String describeSystem(ContactPointSystem system) { 1538 if (system == null) 1539 return ""; 1540 switch (system) { 1541 case PHONE: return "ph: "; 1542 case FAX: return "fax: "; 1543 default: 1544 return ""; 1545 } 1546 } 1547 1548 protected String displayQuantity(Quantity q) { 1549 StringBuilder s = new StringBuilder(); 1550 1551 s.append(q.hasValue() ? q.getValue() : "?"); 1552 if (q.hasUnit()) 1553 s.append(" ").append(q.getUnit()); 1554 else if (q.hasCode()) 1555 s.append(" ").append(q.getCode()); 1556 1557 return s.toString(); 1558 } 1559 1560 protected void renderQuantity(XhtmlNode x, Quantity q) { 1561 renderQuantity(x, q, false); 1562 } 1563 1564 protected void renderQuantity(XhtmlNode x, Quantity q, boolean showCodeDetails) { 1565 if (q.hasComparator()) 1566 x.addText(q.getComparator().toCode()); 1567 if (q.hasValue()) { 1568 x.addText(context.getTranslated(q.getValueElement())); 1569 } 1570 if (q.hasUnit()) 1571 x.tx(" "+context.getTranslated(q.getUnitElement())); 1572 else if (q.hasCode() && q.hasSystem()) { 1573 // if there's a code there *shall* be a system, so if we've got one and not the other, things are invalid and we won't bother trying to render 1574 if (q.hasSystem() && q.getSystem().equals("http://unitsofmeasure.org")) 1575 x.tx(" "+q.getCode()); 1576 else 1577 x.tx("(unit "+q.getCode()+" from "+q.getSystem()+")"); 1578 } 1579 if (showCodeDetails && q.hasCode()) { 1580 x.span("background: LightGoldenRodYellow", null).tx(" "+ (context.formatPhrase(RenderingContext.DATA_REND_DETAILS, displaySystem(q.getSystem()))) +q.getCode()+" = '"+lookupCode(q.getSystem(), null, q.getCode())+"')"); 1581 } 1582 } 1583 1584 1585 protected void renderQuantity(HierarchicalTableGenerator gen, List<Piece> pieces, Quantity q, boolean showCodeDetails) { 1586 pieces.add(gen.new Piece(null, displayQuantity(q), null)); 1587 } 1588 1589 public String displayRange(Range q) { 1590 if (!q.hasLow() && !q.hasHigh()) 1591 return "?"; 1592 1593 StringBuilder b = new StringBuilder(); 1594 1595 boolean sameUnits = (q.getLow().hasUnit() && q.getHigh().hasUnit() && q.getLow().getUnit().equals(q.getHigh().getUnit())) 1596 || (q.getLow().hasCode() && q.getHigh().hasCode() && q.getLow().getCode().equals(q.getHigh().getCode())); 1597 String low = "?"; 1598 if (q.hasLow() && q.getLow().hasValue()) 1599 low = sameUnits ? q.getLow().getValue().toString() : displayQuantity(q.getLow()); 1600 String high = displayQuantity(q.getHigh()); 1601 if (high.isEmpty()) 1602 high = "?"; 1603 b.append(low).append("\u00A0to\u00A0").append(high); 1604 return b.toString(); 1605 } 1606 1607 protected void renderRange(XhtmlNode x, Range q) { 1608 if (q.hasLow()) 1609 x.addText(q.getLow().getValue().toString()); 1610 else 1611 x.tx("?"); 1612 x.tx("-"); 1613 if (q.hasHigh()) 1614 x.addText(q.getHigh().getValue().toString()); 1615 else 1616 x.tx("?"); 1617 if (q.getLow().hasUnit()) 1618 x.tx(" "+q.getLow().getUnit()); 1619 } 1620 1621 public String displayPeriod(Period p) { 1622 String s = !p.hasStart() ? "(?)" : displayDateTime(p.getStartElement()); 1623 s = s + " --> "; 1624 return s + (!p.hasEnd() ? context.formatPhrase(RenderingContext.DATA_REND_ONGOING) : displayDateTime(p.getEndElement())); 1625 } 1626 1627 public void renderPeriod(XhtmlNode x, Period p) { 1628 x.addText(!p.hasStart() ? "??" : displayDateTime(p.getStartElement())); 1629 x.tx(" --> "); 1630 x.addText(!p.hasEnd() ? context.formatPhrase(RenderingContext.DATA_REND_ONGOING) : displayDateTime(p.getEndElement())); 1631 } 1632 1633 public void renderUsageContext(XhtmlNode x, UsageContext u) throws FHIRFormatError, DefinitionException, IOException { 1634 renderCoding(x, u.getCode()); 1635 x.tx(": "); 1636 render(x, u.getValue()); 1637 } 1638 1639 1640 public void renderTriggerDefinition(XhtmlNode x, TriggerDefinition td) throws FHIRFormatError, DefinitionException, IOException { 1641 if (x.isPara()) { 1642 x.b().tx(context.formatPhrase(RenderingContext.GENERAL_TYPE)); 1643 x.tx(": "); 1644 x.tx(td.getType().getDisplay()); 1645 1646 if (td.hasName()) { 1647 x.tx(", "); 1648 x.b().tx(context.formatPhrase(RenderingContext.GENERAL_NAME)); 1649 x.tx(": "); 1650 x.tx(context.getTranslated(td.getNameElement())); 1651 } 1652 if (td.hasCode()) { 1653 x.tx(", "); 1654 x.b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); 1655 x.tx(": "); 1656 renderCodeableConcept(x, td.getCode()); 1657 } 1658 if (td.hasTiming()) { 1659 x.tx(", "); 1660 x.b().tx(context.formatPhrase(RenderingContext.DATA_REND_TIMING)); 1661 x.tx(": "); 1662 render(x, td.getTiming()); 1663 } 1664 if (td.hasCondition()) { 1665 x.tx(", "); 1666 x.b().tx(context.formatPhrase(RenderingContext.DATA_REND_COND)); 1667 x.tx(": "); 1668 renderExpression(x, td.getCondition()); 1669 } 1670 } else { 1671 XhtmlNode tbl = x.table("grid"); 1672 1673 XhtmlNode tr = tbl.tr(); 1674 tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_TYPE)); 1675 tr.td().tx(td.getType().getDisplay()); 1676 1677 if (td.hasName()) { 1678 tr = tbl.tr(); 1679 tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_NAME)); 1680 tr.td().tx(context.getTranslated(td.getNameElement())); 1681 } 1682 if (td.hasCode()) { 1683 tr = tbl.tr(); 1684 tr.td().b().tx(context.formatPhrase(RenderingContext.GENERAL_CODE)); 1685 renderCodeableConcept(tr.td(), td.getCode()); 1686 } 1687 if (td.hasTiming()) { 1688 tr = tbl.tr(); 1689 tr.td().b().tx(context.formatPhrase(RenderingContext.DATA_REND_TIMING)); 1690 render(tr.td(), td.getTiming()); 1691 } 1692 if (td.hasCondition()) { 1693 tr = tbl.tr(); 1694 tr.td().b().tx(context.formatPhrase(RenderingContext.DATA_REND_COND)); 1695 renderExpression(tr.td(), td.getCondition()); 1696 } 1697 } 1698 } 1699 1700 public void renderDataRequirement(XhtmlNode x, DataRequirement dr) throws FHIRFormatError, DefinitionException, IOException { 1701 XhtmlNode tbl = x.table("grid"); 1702 XhtmlNode tr = tbl.tr(); 1703 XhtmlNode td = tr.td().colspan("2"); 1704 td.b().tx(context.formatPhrase(RenderingContext.GENERAL_TYPE)); 1705 td.tx(": "); 1706 StructureDefinition sd = context.getWorker().fetchTypeDefinition(dr.getType().toCode()); 1707 if (sd != null && sd.hasWebPath()) { 1708 td.ah(sd.getWebPath()).tx(dr.getType().toCode()); 1709 } else { 1710 td.tx(dr.getType().toCode()); 1711 } 1712 if (dr.hasProfile()) { 1713 td.tx(" ("); 1714 boolean first = true; 1715 for (CanonicalType p : dr.getProfile()) { 1716 if (first) first = false; else td.tx(" | "); 1717 sd = context.getWorker().fetchResource(StructureDefinition.class, p.getValue()); 1718 if (sd != null && sd.hasWebPath()) { 1719 td.ah(sd.getWebPath()).tx(crPresent(sd)); 1720 } else { 1721 td.tx(p.asStringValue()); 1722 } 1723 } 1724 td.tx(")"); 1725 } 1726 if (dr.hasSubject()) { 1727 tr = tbl.tr(); 1728 td = tr.td().colspan("2"); 1729 td.b().tx(context.formatPhrase(RenderingContext.GENERAL_SUBJ)); 1730 if (dr.hasSubjectReference()) { 1731 renderReference(td, dr.getSubjectReference()); 1732 } else { 1733 renderCodeableConcept(td, dr.getSubjectCodeableConcept()); 1734 } 1735 } 1736 if (dr.hasCodeFilter() || dr.hasDateFilter()) { 1737 tr = tbl.tr().backgroundColor("#efefef"); 1738 tr.td().tx(context.formatPhrase(RenderingContext.GENERAL_FILTER)); 1739 tr.td().tx(context.formatPhrase(RenderingContext.GENERAL_VALUE)); 1740 } 1741 for (DataRequirementCodeFilterComponent cf : dr.getCodeFilter()) { 1742 tr = tbl.tr(); 1743 if (cf.hasPath()) { 1744 tr.td().tx(cf.getPath()); 1745 } else { 1746 tr.td().tx(context.formatPhrase(RenderingContext.DATA_REND_SEARCH, cf.getSearchParam()) + " "); 1747 } 1748 if (cf.hasValueSet()) { 1749 td = tr.td(); 1750 td.tx(context.formatPhrase(RenderingContext.DATA_REND_VALUESET) + " "); 1751 render(td, cf.getValueSetElement()); 1752 } else { 1753 boolean first = true; 1754 td = tr.td(); 1755 td.tx(context.formatPhrase(RenderingContext.DATA_REND_THESE_CODES) + " "); 1756 for (Coding c : cf.getCode()) { 1757 if (first) first = false; else td.tx(", "); 1758 render(td, c); 1759 } 1760 } 1761 } 1762 for (DataRequirementDateFilterComponent cf : dr.getDateFilter()) { 1763 tr = tbl.tr(); 1764 if (cf.hasPath()) { 1765 tr.td().tx(cf.getPath()); 1766 } else { 1767 tr.td().tx(context.formatPhrase(RenderingContext.DATA_REND_SEARCH, cf.getSearchParam()) + " "); 1768 } 1769 render(tr.td(), cf.getValue()); 1770 } 1771 if (dr.hasSort() || dr.hasLimit()) { 1772 tr = tbl.tr(); 1773 td = tr.td().colspan("2"); 1774 if (dr.hasLimit()) { 1775 td.b().tx(context.formatPhrase(RenderingContext.DATA_REND_LIMIT)); 1776 td.tx(": "); 1777 td.tx(dr.getLimit()); 1778 if (dr.hasSort()) { 1779 td.tx(", "); 1780 } 1781 } 1782 if (dr.hasSort()) { 1783 td.b().tx(context.formatPhrase(RenderingContext.DATA_REND_SORT)); 1784 td.tx(": "); 1785 boolean first = true; 1786 for (DataRequirementSortComponent p : dr.getSort()) { 1787 if (first) first = false; else td.tx(" | "); 1788 td.tx(p.getDirection() == SortDirection.ASCENDING ? "+" : "-"); 1789 td.tx(p.getPath()); 1790 } 1791 } 1792 } 1793 } 1794 1795 1796 private String displayTiming(Timing s) throws FHIRException { 1797 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 1798 if (s.hasCode()) 1799 b.append(context.formatPhrase(RenderingContext.GENERAL_CODE, displayCodeableConcept(s.getCode())) + " "); 1800 1801 if (s.getEvent().size() > 0) { 1802 CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder(); 1803 for (DateTimeType p : s.getEvent()) { 1804 if (p.hasValue()) { 1805 c.append(displayDateTime(p)); 1806 } else if (!renderExpression(c, p)) { 1807 c.append("??"); 1808 } 1809 } 1810 b.append(context.formatPhrase(RenderingContext.DATA_REND_EVENTS, c.toString()) + " "); 1811 } 1812 1813 if (s.hasRepeat()) { 1814 TimingRepeatComponent rep = s.getRepeat(); 1815 if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasStart()) 1816 b.append(context.formatPhrase(RenderingContext.DATA_REND_STARTING, displayDateTime(rep.getBoundsPeriod().getStartElement())) + " "); 1817 if (rep.hasCount()) 1818 b.append(context.formatPhrase(RenderingContext.DATA_REND_COUNT, Integer.toString(rep.getCount())) + " " + " times"); 1819 if (rep.hasDuration()) 1820 b.append(context.formatPhrase(RenderingContext.DATA_REND_DURATION, rep.getDuration().toPlainString()+displayTimeUnits(rep.getPeriodUnit())) + " "); 1821 1822 if (rep.hasWhen()) { 1823 String st = ""; 1824 if (rep.hasOffset()) { 1825 st = Integer.toString(rep.getOffset())+"min "; 1826 } 1827 b.append(st); 1828 for (Enumeration<EventTiming> wh : rep.getWhen()) 1829 b.append(displayEventCode(wh.getValue())); 1830 } else { 1831 String st = ""; 1832 if (!rep.hasFrequency() || (!rep.hasFrequencyMax() && rep.getFrequency() == 1) ) 1833 st = context.formatPhrase(RenderingContext.DATA_REND_ONCE); 1834 else { 1835 st = Integer.toString(rep.getFrequency()); 1836 if (rep.hasFrequencyMax()) 1837 st = st + "-"+Integer.toString(rep.getFrequency()); 1838 } 1839 if (rep.hasPeriod()) { 1840 st = st + " "+ (context.formatPhrase(RenderingContext.DATA_REND_PER))+rep.getPeriod().toPlainString(); 1841 if (rep.hasPeriodMax()) 1842 st = st + "-"+rep.getPeriodMax().toPlainString(); 1843 st = st + " "+displayTimeUnits(rep.getPeriodUnit()); 1844 } 1845 b.append(st); 1846 } 1847 if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasEnd()) 1848 b.append(context.formatPhrase(RenderingContext.DATA_REND_UNTIL, displayDateTime(rep.getBoundsPeriod().getEndElement())) + " "); 1849 } 1850 return b.toString(); 1851 } 1852 1853 private boolean renderExpression(CommaSeparatedStringBuilder c, PrimitiveType p) { 1854 Extension exp = p.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/cqf-expression"); 1855 if (exp == null) { 1856 return false; 1857 } 1858 c.append(exp.getValueExpression().getExpression()); 1859 return true; 1860 } 1861 1862 private String displayEventCode(EventTiming when) { 1863 switch (when) { 1864 case C: return (context.formatPhrase(RenderingContext.DATA_REND_MEALS)); 1865 case CD: return (context.formatPhrase(RenderingContext.DATA_REND_ATLUNCH)); 1866 case CM: return (context.formatPhrase(RenderingContext.DATA_REND_ATBKFST)); 1867 case CV: return (context.formatPhrase(RenderingContext.DATA_REND_ATDINR)); 1868 case AC: return (context.formatPhrase(RenderingContext.DATA_REND_BFMEALS)); 1869 case ACD: return (context.formatPhrase(RenderingContext.DATA_REND_BFLUNCH)); 1870 case ACM: return (context.formatPhrase(RenderingContext.DATA_REND_BFBKFST)); 1871 case ACV: return (context.formatPhrase(RenderingContext.DATA_REND_BFDINR)); 1872 case HS: return (context.formatPhrase(RenderingContext.DATA_REND_BFSLEEP)); 1873 case PC: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRMEALS)); 1874 case PCD: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRLUNCH)); 1875 case PCM: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRBKFST)); 1876 case PCV: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRDINR)); 1877 case WAKE: return (context.formatPhrase(RenderingContext.DATA_REND_AFTRWKNG)); 1878 default: return "?ngen-6?"; 1879 } 1880 } 1881 1882 private String displayTimeUnits(UnitsOfTime units) { 1883 if (units == null) 1884 return "?ngen-7?"; 1885 switch (units) { 1886 case A: return "years"; 1887 case D: return "days"; 1888 case H: return "hours"; 1889 case MIN: return "minutes"; 1890 case MO: return "months"; 1891 case S: return "seconds"; 1892 case WK: return "weeks"; 1893 default: return "?ngen-8?"; 1894 } 1895 } 1896 1897 protected void renderTiming(XhtmlNode x, Timing s) throws FHIRException { 1898 x.addText(displayTiming(s)); 1899 } 1900 1901 1902 private String displaySampledData(SampledData s) { 1903 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 1904 if (s.hasOrigin()) 1905 b.append(context.formatPhrase(RenderingContext.DATA_REND_ORIGIN, displayQuantity(s.getOrigin())) + " "); 1906 1907 if (s.hasInterval()) { 1908 b.append(context.formatPhrase(RenderingContext.DATA_REND_INT, s.getInterval().toString()) + " "); 1909 1910 if (s.hasIntervalUnit()) 1911 b.append(s.getIntervalUnit().toString()); 1912 } 1913 1914 if (s.hasFactor()) 1915 b.append(context.formatPhrase(RenderingContext.DATA_REND_FACT, s.getFactor().toString()) + " "); 1916 1917 if (s.hasLowerLimit()) 1918 b.append(context.formatPhrase(RenderingContext.DATA_REND_LOWER, s.getLowerLimit().toString()) + " "); 1919 1920 if (s.hasUpperLimit()) 1921 b.append(context.formatPhrase(RenderingContext.DATA_REND_UP, s.getUpperLimit().toString()) + " "); 1922 1923 if (s.hasDimensions()) 1924 b.append(context.formatPhrase(RenderingContext.DATA_REND_DIM, s.getDimensions()) + " "); 1925 1926 if (s.hasData()) 1927 b.append(context.formatPhrase(RenderingContext.DATA_REND_DATA, s.getData()) + " "); 1928 1929 return b.toString(); 1930 } 1931 1932 protected void renderSampledData(XhtmlNode x, SampledData sampledData) { 1933 x.addText(displaySampledData(sampledData)); 1934 } 1935 1936 public RenderingContext getContext() { 1937 return context; 1938 } 1939 1940 1941 public XhtmlNode makeExceptionXhtml(Exception e, String function) { 1942 XhtmlNode xn; 1943 xn = new XhtmlNode(NodeType.Element, "div"); 1944 XhtmlNode p = xn.para(); 1945 p.b().tx((context.formatPhrase(RenderingContext.DATA_REND_EXCEPTION)) +function+": "+e.getMessage()); 1946 p.addComment(getStackTrace(e)); 1947 return xn; 1948 } 1949 1950 private String getStackTrace(Exception e) { 1951 StringBuilder b = new StringBuilder(); 1952 b.append("\r\n"); 1953 for (StackTraceElement t : e.getStackTrace()) { 1954 b.append(t.getClassName()+"."+t.getMethodName()+" ("+t.getFileName()+":"+t.getLineNumber()); 1955 b.append("\r\n"); 1956 } 1957 return b.toString(); 1958 } 1959 1960 protected String versionFromCanonical(String system) { 1961 if (system == null) { 1962 return null; 1963 } else if (system.contains("|")) { 1964 return system.substring(0, system.indexOf("|")); 1965 } else { 1966 return null; 1967 } 1968 } 1969 1970 protected String systemFromCanonical(String system) { 1971 if (system == null) { 1972 return null; 1973 } else if (system.contains("|")) { 1974 return system.substring(system.indexOf("|")+1); 1975 } else { 1976 return system; 1977 } 1978 } 1979 1980 1981}