001package org.hl7.fhir.r5.renderers; 002 003import java.io.IOException; 004import java.io.UnsupportedEncodingException; 005import java.util.ArrayList; 006import java.util.HashMap; 007import java.util.HashSet; 008import java.util.List; 009import java.util.Map; 010import java.util.Set; 011 012import org.apache.commons.codec.binary.Base64; 013import org.apache.commons.lang3.NotImplementedException; 014import org.hl7.fhir.exceptions.DefinitionException; 015import org.hl7.fhir.exceptions.FHIRException; 016import org.hl7.fhir.exceptions.FHIRFormatError; 017import org.hl7.fhir.r5.context.ContextUtilities; 018import org.hl7.fhir.r5.model.Address; 019import org.hl7.fhir.r5.model.Annotation; 020import org.hl7.fhir.r5.model.Attachment; 021import org.hl7.fhir.r5.model.Base; 022import org.hl7.fhir.r5.model.Base64BinaryType; 023import org.hl7.fhir.r5.model.BooleanType; 024import org.hl7.fhir.r5.model.CodeType; 025import org.hl7.fhir.r5.model.CodeableConcept; 026import org.hl7.fhir.r5.model.CodeableReference; 027import org.hl7.fhir.r5.model.Coding; 028import org.hl7.fhir.r5.model.ContactDetail; 029import org.hl7.fhir.r5.model.ContactPoint; 030import org.hl7.fhir.r5.model.DataRequirement; 031import org.hl7.fhir.r5.model.DataType; 032import org.hl7.fhir.r5.model.DateTimeType; 033import org.hl7.fhir.r5.model.DomainResource; 034import org.hl7.fhir.r5.model.Dosage; 035import org.hl7.fhir.r5.model.ElementDefinition; 036import org.hl7.fhir.r5.model.Enumeration; 037import org.hl7.fhir.r5.model.Expression; 038import org.hl7.fhir.r5.model.Extension; 039import org.hl7.fhir.r5.model.HumanName; 040import org.hl7.fhir.r5.model.IdType; 041import org.hl7.fhir.r5.model.Identifier; 042import org.hl7.fhir.r5.model.InstantType; 043import org.hl7.fhir.r5.model.Meta; 044import org.hl7.fhir.r5.model.Money; 045import org.hl7.fhir.r5.model.Narrative; 046import org.hl7.fhir.r5.model.Narrative.NarrativeStatus; 047import org.hl7.fhir.r5.model.Period; 048import org.hl7.fhir.r5.model.PrimitiveType; 049import org.hl7.fhir.r5.model.Property; 050import org.hl7.fhir.r5.model.Quantity; 051import org.hl7.fhir.r5.model.Range; 052import org.hl7.fhir.r5.model.Ratio; 053import org.hl7.fhir.r5.model.Reference; 054import org.hl7.fhir.r5.model.RelatedArtifact; 055import org.hl7.fhir.r5.model.Resource; 056import org.hl7.fhir.r5.model.SampledData; 057import org.hl7.fhir.r5.model.Signature; 058import org.hl7.fhir.r5.model.StringType; 059import org.hl7.fhir.r5.model.StructureDefinition; 060import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; 061import org.hl7.fhir.r5.model.Timing; 062import org.hl7.fhir.r5.model.UriType; 063import org.hl7.fhir.r5.model.UsageContext; 064import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; 065import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper; 066import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; 067import org.hl7.fhir.r5.renderers.utils.DirectWrappers; 068import org.hl7.fhir.r5.renderers.utils.DirectWrappers.BaseWrapperDirect; 069import org.hl7.fhir.r5.renderers.utils.DirectWrappers.PropertyWrapperDirect; 070import org.hl7.fhir.r5.renderers.utils.DirectWrappers.ResourceWrapperDirect; 071import org.hl7.fhir.r5.renderers.utils.ElementWrappers; 072import org.hl7.fhir.r5.renderers.utils.RenderingContext; 073import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; 074import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceWithReference; 075import org.hl7.fhir.r5.utils.EOperationOutcome; 076import org.hl7.fhir.r5.utils.ToolingExtensions; 077import org.hl7.fhir.r5.utils.XVerExtensionManager; 078import org.hl7.fhir.r5.utils.XVerExtensionManager.XVerExtensionStatus; 079import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; 080import org.hl7.fhir.utilities.Utilities; 081import org.hl7.fhir.utilities.xhtml.NodeType; 082import org.hl7.fhir.utilities.xhtml.XhtmlNode; 083 084public class ProfileDrivenRenderer extends ResourceRenderer { 085 086 private Set<String> containedIds = new HashSet<>(); 087 private boolean hasExtensions; 088 089 public ProfileDrivenRenderer(RenderingContext context, ResourceContext rcontext) { 090 super(context, rcontext); 091 } 092 093 public ProfileDrivenRenderer(RenderingContext context) { 094 super(context); 095 } 096 097 @Override 098 public boolean render(XhtmlNode x, Resource r) throws FHIRFormatError, DefinitionException, IOException { 099 return render(x, new DirectWrappers.ResourceWrapperDirect(context, r)); 100 } 101 102 @Override 103 public boolean render(XhtmlNode x, ResourceWrapper r) throws FHIRFormatError, DefinitionException, IOException { 104 boolean idDone = false; 105 XhtmlNode p = x.para(); 106 if (context.isAddGeneratedNarrativeHeader()) { 107 p.b().tx("Generated Narrative: "+r.fhirType()+(context.isContained() ? " #"+r.getId() : "")); 108 if (!Utilities.noString(r.getId())) { 109 p.an(r.getId()); 110 } 111 idDone = true; 112 } 113 if (context.isTechnicalMode() && !context.isContained()) { 114 renderResourceHeader(r, x, !idDone); 115 idDone = true; 116 } 117 if (!Utilities.noString(r.getId()) && !idDone) { 118 x.para().an(r.getId()); 119 } 120 try { 121 StructureDefinition sd = r.getDefinition(); 122 if (sd == null) { 123 throw new FHIRException("Cannot find definition for "+r.fhirType()); 124 } else { 125 ElementDefinition ed = sd.getSnapshot().getElement().get(0); 126 containedIds.clear(); 127 hasExtensions = false; 128 generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), context.isTechnicalMode(), 0); 129 } 130 } catch (Exception e) { 131 System.out.println("Error Generating Narrative for "+r.fhirType()+"/"+r.getId()+": "+e.getMessage()); 132 e.printStackTrace(); 133 x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage()); 134 } 135 return hasExtensions; 136 } 137 138 139 @Override 140 public String display(Resource r) throws UnsupportedEncodingException, IOException { 141 return "todo"; 142 } 143 144 @Override 145 public String display(ResourceWrapper res) throws UnsupportedEncodingException, IOException { 146 StructureDefinition profile = getContext().getWorker().fetchTypeDefinition(res.fhirType()); 147 if (profile == null) 148 return "unknown resource type " +res.fhirType(); 149 else { 150 boolean firstElement = true; 151 boolean last = false; 152 List<PropertyWrapper> children = res.children(); 153 ContextUtilities cu = new ContextUtilities(context.getWorker()); 154 for (PropertyWrapper p : children) { 155 if (p.getName().equals("title") && cu.isDatatype(p.fhirType()) && p.hasValues()) { 156 return res.fhirType()+" "+ display((DataType) p.getValues().get(0).getBase()); 157 } 158 } 159 for (PropertyWrapper p : children) { 160 if (p.getName().equals("name") && cu.isDatatype(p.fhirType()) && p.hasValues()) { 161 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 162 for (BaseWrapper v : p.getValues()) { 163 b.append((display((DataType) v.getBase()))); 164 } 165 return res.fhirType()+" "+ b.toString(); 166 } 167 } 168 for (PropertyWrapper p : children) { 169 if (p.getName().equals("code") && cu.isDatatype(p.fhirType()) && p.hasValues()) { 170 CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); 171 for (BaseWrapper v : p.getValues()) { 172 b.append((display((DataType) v.getBase()))); 173 } 174 return res.fhirType()+" "+ b.toString(); 175 } 176 } 177 for (PropertyWrapper p : children) { 178 StringBuilder b = new StringBuilder(); 179 if (!ignoreProperty(p) && !p.getElementDefinition().getBase().getPath().startsWith("Resource.")) { 180 ElementDefinition child = getElementDefinition(profile.getSnapshot().getElement(), res.fhirType()+"."+p.getName(), p); 181 if (p.getValues().size() > 0 && p.getValues().get(0) != null && child != null && isSimple(child) && includeInSummary(child, p.getValues())) { 182 if (firstElement) 183 firstElement = false; 184 else if (last) 185 b.append("; "); 186 boolean first = true; 187 last = false; 188 for (BaseWrapper v : p.getValues()) { 189 if (first) 190 first = false; 191 else if (last) 192 b.append(", "); 193 b.append((display((DataType) v.getBase()))); 194 } 195 } 196 } 197 return res.fhirType()+" "+ b.toString(); 198 } 199 return res.fhirType()+" ???"; 200 } 201 } 202 203// 204// public void inject(Element er, XhtmlNode x, NarrativeStatus status, boolean pretty) { 205// if (!x.hasAttribute("xmlns")) 206// x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); 207// Element le = XMLUtil.getNamedChild(er, "language"); 208// String l = le == null ? null : le.getAttribute("value"); 209// if (!Utilities.noString(l)) { 210// // use both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues 211// x.setAttribute("lang", l); 212// x.setAttribute("xml:lang", l); 213// } 214// Element txt = XMLUtil.getNamedChild(er, "text"); 215// if (txt == null) { 216// txt = er.getOwnerDocument().createElementNS(FormatUtilities.FHIR_NS, "text"); 217// Element n = XMLUtil.getFirstChild(er); 218// while (n != null && (n.getNodeName().equals("id") || n.getNodeName().equals("meta") || n.getNodeName().equals("implicitRules") || n.getNodeName().equals("language"))) 219// n = XMLUtil.getNextSibling(n); 220// if (n == null) 221// er.appendChild(txt); 222// else 223// er.insertBefore(txt, n); 224// } 225// Element st = XMLUtil.getNamedChild(txt, "status"); 226// if (st == null) { 227// st = er.getOwnerDocument().createElementNS(FormatUtilities.FHIR_NS, "status"); 228// Element n = XMLUtil.getFirstChild(txt); 229// if (n == null) 230// txt.appendChild(st); 231// else 232// txt.insertBefore(st, n); 233// } 234// st.setAttribute("value", status.toCode()); 235// Element div = XMLUtil.getNamedChild(txt, "div"); 236// if (div == null) { 237// div = er.getOwnerDocument().createElementNS(FormatUtilities.XHTML_NS, "div"); 238// div.setAttribute("xmlns", FormatUtilities.XHTML_NS); 239// txt.appendChild(div); 240// } 241// if (div.hasChildNodes()) 242// div.appendChild(er.getOwnerDocument().createElementNS(FormatUtilities.XHTML_NS, "hr")); 243// new XhtmlComposer(XhtmlComposer.XML, pretty).compose(div, x); 244// } 245// 246// public void inject(org.hl7.fhir.r5.elementmodel.Element er, XhtmlNode x, NarrativeStatus status, boolean pretty) throws IOException, FHIRException { 247// if (!x.hasAttribute("xmlns")) 248// x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); 249// String l = er.getChildValue("language"); 250// if (!Utilities.noString(l)) { 251// // use both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues 252// x.setAttribute("lang", l); 253// x.setAttribute("xml:lang", l); 254// } 255// org.hl7.fhir.r5.elementmodel.Element txt = er.getNamedChild("text"); 256// if (txt == null) { 257// txt = new org.hl7.fhir.r5.elementmodel.Element("text", er.getProperty().getChild(null, "text")); 258// int i = 0; 259// while (i < er.getChildren().size() && (er.getChildren().get(i).getName().equals("id") || er.getChildren().get(i).getName().equals("meta") || er.getChildren().get(i).getName().equals("implicitRules") || er.getChildren().get(i).getName().equals("language"))) 260// i++; 261// if (i >= er.getChildren().size()) 262// er.getChildren().add(txt); 263// else 264// er.getChildren().add(i, txt); 265// } 266// org.hl7.fhir.r5.elementmodel.Element st = txt.getNamedChild("status"); 267// if (st == null) { 268// st = new org.hl7.fhir.r5.elementmodel.Element("status", txt.getProperty().getChild(null, "status")); 269// txt.getChildren().add(0, st); 270// } 271// st.setValue(status.toCode()); 272// org.hl7.fhir.r5.elementmodel.Element div = txt.getNamedChild("div"); 273// if (div == null) { 274// div = new org.hl7.fhir.r5.elementmodel.Element("div", txt.getProperty().getChild(null, "div")); 275// txt.getChildren().add(div); 276// div.setValue(new XhtmlComposer(XhtmlComposer.XML, pretty).compose(x)); 277// } 278// div.setValue(x.toString()); 279// div.setXhtml(x); 280// } 281// 282 283 284 public void generateResourceSummary(XhtmlNode x, ResourceWrapper res, boolean textAlready, boolean showCodeDetails, boolean canLink) throws FHIRException, UnsupportedEncodingException, IOException { 285 if (!textAlready) { 286 XhtmlNode div = res.getNarrative(); 287 if (div != null) { 288 if (div.allChildrenAreText()) 289 x.getChildNodes().addAll(div.getChildNodes()); 290 if (div.getChildNodes().size() == 1 && div.getChildNodes().get(0).allChildrenAreText()) 291 x.getChildNodes().addAll(div.getChildNodes().get(0).getChildNodes()); 292 } 293 x.tx("Generated Summary: "); 294 } 295 String path = res.fhirType(); 296 StructureDefinition profile = getContext().getWorker().fetchResource(StructureDefinition.class, path); 297 if (profile == null) 298 x.tx("unknown resource " +path); 299 else { 300 boolean firstElement = true; 301 boolean last = false; 302 for (PropertyWrapper p : res.children()) { 303 if (!ignoreProperty(p) && !p.getElementDefinition().getBase().getPath().startsWith("Resource.")) { 304 ElementDefinition child = getElementDefinition(profile.getSnapshot().getElement(), path+"."+p.getName(), p); 305 if (p.getValues().size() > 0 && p.getValues().get(0) != null && child != null && isSimple(child) && includeInSummary(child, p.getValues())) { 306 if (firstElement) 307 firstElement = false; 308 else if (last) 309 x.tx("; "); 310 boolean first = true; 311 last = false; 312 for (BaseWrapper v : p.getValues()) { 313 if (first) 314 first = false; 315 else if (last) 316 x.tx(", "); 317 last = displayLeaf(res, v, child, x, p.getName(), showCodeDetails, canLink) || last; 318 } 319 } 320 } 321 } 322 } 323 } 324 325 326 private boolean ignoreProperty(PropertyWrapper p) { 327 return Utilities.existsInList(p.getName(), "contained"); 328 } 329 330 private boolean includeInSummary(ElementDefinition child, List<BaseWrapper> list) throws UnsupportedEncodingException, FHIRException, IOException { 331 if (child.getName().endsWith("active") && list != null && list.size() > 0 && "true".equals(list.get(0).getBase().primitiveValue())) { 332 return false; 333 } 334 if (child.getIsModifier()) 335 return true; 336 if (child.getMustSupport()) 337 return true; 338 if (child.getType().size() == 1) { 339 String t = child.getType().get(0).getWorkingCode(); 340 if (t.equals("Address") || t.equals("Contact") || t.equals("Reference") || t.equals("Uri") || t.equals("Url") || t.equals("Canonical")) 341 return false; 342 } 343 return true; 344 } 345 346 private ElementDefinition getElementDefinition(List<ElementDefinition> elements, String path, PropertyWrapper p) { 347 for (ElementDefinition element : elements) 348 if (element.getPath().equals(path)) 349 return element; 350 if (path.endsWith("\"]") && p.getStructure() != null) 351 return p.getStructure().getSnapshot().getElement().get(0); 352 return null; 353 } 354 355 private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode parent, XhtmlNode x, boolean title, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome { 356 if (ew == null) 357 return; 358 359 Base e = ew.getBase(); 360 if (context.isShowComments()) { 361 x = renderCommentsSpan(x, e); 362 } 363 364 if (e instanceof StringType) 365 x.addText(((StringType) e).getValue()); 366 else if (e instanceof CodeType) 367 x.addText(((CodeType) e).getValue()); 368 else if (e instanceof IdType) 369 x.addText(((IdType) e).getValue()); 370 else if (e instanceof Extension) 371 return; 372 else if (e instanceof InstantType) 373 x.addText(((InstantType) e).toHumanDisplay()); 374 else if (e instanceof DateTimeType) { 375 renderDateTime(x, e); 376 } else if (e instanceof Base64BinaryType) 377 x.addText(new Base64().encodeAsString(((Base64BinaryType) e).getValue())); 378 else if (e instanceof org.hl7.fhir.r5.model.DateType) { 379 org.hl7.fhir.r5.model.DateType dt = ((org.hl7.fhir.r5.model.DateType) e); 380 renderDate(x, dt); 381 } else if (e instanceof Enumeration) { 382 Object ev = ((Enumeration<?>) e).getValue(); 383 x.addText(ev == null ? "" : ev.toString()); // todo: look up a display name if there is one 384 } else if (e instanceof BooleanType) { 385 x.addText(((BooleanType) e).getValue().toString()); 386 } else if (e instanceof CodeableConcept) { 387 renderCodeableConcept(x, (CodeableConcept) e, showCodeDetails); 388 } else if (e instanceof Coding) { 389 renderCoding(x, (Coding) e, showCodeDetails); 390 } else if (e instanceof CodeableReference) { 391 renderCodeableReference(x, (CodeableReference) e, showCodeDetails); 392 } else if (e instanceof Annotation) { 393 renderAnnotation(x, (Annotation) e); 394 } else if (e instanceof Identifier) { 395 renderIdentifier(x, (Identifier) e); 396 } else if (e instanceof org.hl7.fhir.r5.model.IntegerType) { 397 if (((org.hl7.fhir.r5.model.IntegerType) e).hasValue()) { 398 x.addText(Integer.toString(((org.hl7.fhir.r5.model.IntegerType) e).getValue())); 399 } else { 400 x.addText("??"); 401 } 402 } else if (e instanceof org.hl7.fhir.r5.model.Integer64Type) { 403 if (((org.hl7.fhir.r5.model.Integer64Type) e).hasValue()) { 404 x.addText(Long.toString(((org.hl7.fhir.r5.model.Integer64Type) e).getValue())); 405 } else { 406 x.addText("??"); 407 } 408 } else if (e instanceof org.hl7.fhir.r5.model.DecimalType) { 409 x.addText(((org.hl7.fhir.r5.model.DecimalType) e).getValue().toString()); 410 } else if (e instanceof HumanName) { 411 renderHumanName(x, (HumanName) e); 412 } else if (e instanceof SampledData) { 413 renderSampledData(x, (SampledData) e); 414 } else if (e instanceof Address) { 415 renderAddress(x, (Address) e); 416 } else if (e instanceof ContactPoint) { 417 renderContactPoint(x, (ContactPoint) e); 418 } else if (e instanceof Expression) { 419 renderExpression(x, (Expression) e); 420 } else if (e instanceof Money) { 421 renderMoney(x, (Money) e); 422 } else if (e instanceof ContactDetail) { 423 ContactDetail cd = (ContactDetail) e; 424 if (cd.hasName()) { 425 x.tx(cd.getName()+": "); 426 } 427 boolean first = true; 428 for (ContactPoint c : cd.getTelecom()) { 429 if (first) first = false; else x.tx(","); 430 renderContactPoint(x, c); 431 } 432 } else if (e instanceof UriType) { 433 renderUri(x, (UriType) e, defn.getPath(), rcontext != null && rcontext.getResource() != null ? rcontext.getResource().getId() : null, res.getResource()); 434 } else if (e instanceof Timing) { 435 renderTiming(x, (Timing) e); 436 } else if (e instanceof Range) { 437 renderRange(x, (Range) e); 438 } else if (e instanceof Quantity) { 439 renderQuantity(x, (Quantity) e, showCodeDetails); 440 } else if (e instanceof Ratio) { 441 renderQuantity(x, ((Ratio) e).getNumerator(), showCodeDetails); 442 x.tx("/"); 443 renderQuantity(x, ((Ratio) e).getDenominator(), showCodeDetails); 444 } else if (e instanceof Period) { 445 Period p = (Period) e; 446 renderPeriod(x, p); 447 } else if (e instanceof Reference) { 448 Reference r = (Reference) e; 449 if (r.getReference() != null && r.getReference().contains("#")) { 450 if (containedIds.contains(r.getReference().substring(1))) { 451 x.ah(r.getReference()).tx("See "+r.getReference()); 452 } else { 453 // in this case, we render the resource in line 454 ResourceWrapper rw = null; 455 for (ResourceWrapper t : res.getContained()) { 456 if (r.getReference().substring(1).equals(t.getId())) { 457 rw = t; 458 } 459 } 460 if (rw == null) { 461 renderReference(res, x, r); 462 } else { 463 String ref = context.getResolver() != null ?context.getResolver().urlForContained(context, res.fhirType(), res.getId(), rw.fhirType(), rw.getId()) : null; 464 if (ref == null) { 465 x.an(rw.getId()); 466 RenderingContext ctxtc = context.copy(); 467 ctxtc.setAddGeneratedNarrativeHeader(false); 468 ctxtc.setContained(true); 469 ResourceRenderer rr = RendererFactory.factory(rw, ctxtc); 470 rr.setRcontext(new ResourceContext(rcontext, rw)); 471 rr.render(parent.blockquote(), rw); 472 } else { 473 x.ah(ref).tx("See "+rw.fhirType()); 474 } 475 } 476 } 477 } else { 478 renderReference(res, x, r); 479 } 480 } else if (e instanceof Resource) { 481 return; 482 } else if (e instanceof DataRequirement) { 483 DataRequirement p = (DataRequirement) e; 484 renderDataRequirement(x, p); 485 } else if (e instanceof PrimitiveType) { 486 x.tx(((PrimitiveType) e).primitiveValue()); 487 } else if (e instanceof ElementDefinition) { 488 x.tx("todo-bundle"); 489 } else if (e != null && !(e instanceof Attachment) && !(e instanceof Narrative) && !(e instanceof Meta)) { 490 throw new NotImplementedException("type "+e.getClass().getName()+" not handled - should not be here"); 491 } 492 } 493 494 private XhtmlNode renderCommentsSpan(XhtmlNode x, Base e) { 495 if (e.hasFormatComment()) { 496 return x.span(null, CommaSeparatedStringBuilder.join(" ", e.getFormatCommentsPre())); 497 } else { 498 return x; 499 } 500 } 501 502 private boolean displayLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails) throws FHIRException, UnsupportedEncodingException, IOException { 503 return displayLeaf(res, ew, defn, x, name, showCodeDetails, true); 504 } 505 506 private boolean displayLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, String name, boolean showCodeDetails, boolean allowLinks) throws FHIRException, UnsupportedEncodingException, IOException { 507 if (ew == null) 508 return false; 509 Base e = ew.getBase(); 510 if (e == null) 511 return false; 512 513 Map<String, String> displayHints = readDisplayHints(defn); 514 515 if (name.endsWith("[x]")) 516 name = name.substring(0, name.length() - 3); 517 518 if (!showCodeDetails && e instanceof PrimitiveType && isDefault(displayHints, ((PrimitiveType) e))) 519 return false; 520 521 if (e instanceof StringType) { 522 x.addText(name+": "+((StringType) e).getValue()); 523 return true; 524 } else if (e instanceof CodeType) { 525 x.addText(name+": "+((CodeType) e).getValue()); 526 return true; 527 } else if (e instanceof IdType) { 528 x.addText(name+": "+((IdType) e).getValue()); 529 return true; 530 } else if (e instanceof UriType) { 531 if (Utilities.isAbsoluteUrlLinkable(((UriType) e).getValue()) && allowLinks) { 532 x.tx(name+": "); 533 x.ah(((UriType) e).getValue()).addText(((UriType) e).getValue()); 534 } else { 535 x.addText(name+": "+((UriType) e).getValue()); 536 } 537 return true; 538 } else if (e instanceof DateTimeType) { 539 x.addText(name+": "+((DateTimeType) e).toHumanDisplay()); 540 return true; 541 } else if (e instanceof InstantType) { 542 x.addText(name+": "+((InstantType) e).toHumanDisplay()); 543 return true; 544 } else if (e instanceof Extension) { 545 // x.tx("Extensions: todo"); 546 return false; 547 } else if (e instanceof org.hl7.fhir.r5.model.DateType) { 548 x.addText(name+": "+((org.hl7.fhir.r5.model.DateType) e).toHumanDisplay()); 549 return true; 550 } else if (e instanceof Enumeration) { 551 x.addText(((Enumeration<?>) e).getValue().toString()); // todo: look up a display name if there is one 552 return true; 553 } else if (e instanceof BooleanType) { 554 if (((BooleanType) e).hasValue()) { 555 x.addText(name); 556 x.addText(": "); 557 x.addText(((BooleanType) e).getValueAsString()); 558 return true; 559 } 560 } else if (e instanceof CodeableReference) { 561 if (((CodeableReference) e).hasReference()) { 562 Reference r = ((CodeableReference) e).getReference(); 563 renderReference(res, x, r, allowLinks); 564 } else { 565 renderCodeableConcept(x, ((CodeableReference) e).getConcept(), showCodeDetails); 566 } 567 return true; 568 } else if (e instanceof CodeableConcept) { 569 renderCodeableConcept(x, (CodeableConcept) e, showCodeDetails); 570 return true; 571 } else if (e instanceof Coding) { 572 renderCoding(x, (Coding) e, showCodeDetails); 573 return true; 574 } else if (e instanceof Annotation) { 575 renderAnnotation(x, (Annotation) e, showCodeDetails); 576 return true; 577 } else if (e instanceof org.hl7.fhir.r5.model.IntegerType) { 578 x.addText(Integer.toString(((org.hl7.fhir.r5.model.IntegerType) e).getValue())); 579 return true; 580 } else if (e instanceof org.hl7.fhir.r5.model.DecimalType) { 581 x.addText(((org.hl7.fhir.r5.model.DecimalType) e).getValue().toString()); 582 return true; 583 } else if (e instanceof Identifier) { 584 renderIdentifier(x, (Identifier) e); 585 return true; 586 } else if (e instanceof HumanName) { 587 renderHumanName(x, (HumanName) e); 588 return true; 589 } else if (e instanceof SampledData) { 590 renderSampledData(x, (SampledData) e); 591 return true; 592 } else if (e instanceof Address) { 593 renderAddress(x, (Address) e); 594 return true; 595 } else if (e instanceof ContactPoint) { 596 if (allowLinks) { 597 renderContactPoint(x, (ContactPoint) e); 598 } else { 599 displayContactPoint(x, (ContactPoint) e); 600 } 601 return true; 602 } else if (e instanceof Timing) { 603 renderTiming(x, (Timing) e); 604 return true; 605 } else if (e instanceof Quantity) { 606 renderQuantity(x, (Quantity) e, showCodeDetails); 607 return true; 608 } else if (e instanceof Ratio) { 609 renderQuantity(x, ((Ratio) e).getNumerator(), showCodeDetails); 610 x.tx("/"); 611 renderQuantity(x, ((Ratio) e).getDenominator(), showCodeDetails); 612 return true; 613 } else if (e instanceof Period) { 614 Period p = (Period) e; 615 x.addText(name+": "); 616 x.addText(!p.hasStart() ? "?ngen-2?" : p.getStartElement().toHumanDisplay()); 617 x.tx(" --> "); 618 x.addText(!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay()); 619 return true; 620 } else if (e instanceof Reference) { 621 Reference r = (Reference) e; 622 if (r.hasDisplayElement()) 623 x.addText(r.getDisplay()); 624 else if (r.hasReferenceElement()) { 625 ResourceWithReference tr = resolveReference(res, r.getReference()); 626 x.addText(tr == null ? r.getReference() : "?ngen-3"); // getDisplayForReference(tr.getReference())); 627 } else 628 x.tx("?ngen-4?"); 629 return true; 630 } else if (e instanceof Narrative) { 631 return false; 632 } else if (e instanceof Resource) { 633 return false; 634 } else if (e instanceof ContactDetail) { 635 ContactDetail cd = (ContactDetail) e; 636 if (cd.hasName()) { 637 x.tx(cd.getName()+": "); 638 } 639 boolean first = true; 640 for (ContactPoint c : cd.getTelecom()) { 641 if (first) first = false; else x.tx(","); 642 if (allowLinks) { 643 renderContactPoint(x, c); 644 } else { 645 displayContactPoint(x, c); 646 } 647 } 648 return true; 649 } else if (e instanceof Range) { 650 return false; 651 } else if (e instanceof Meta) { 652 return false; 653 } else if (e instanceof Dosage) { 654 return false; 655 } else if (e instanceof Signature) { 656 return false; 657 } else if (e instanceof UsageContext) { 658 return false; 659 } else if (e instanceof RelatedArtifact) { 660 return false; 661 } else if (e instanceof ElementDefinition) { 662 return false; 663 } else if (e instanceof Base64BinaryType) { 664 return false; 665 } else if (!(e instanceof Attachment)) 666 throw new NotImplementedException("type "+e.getClass().getName()+" not handled yet"); 667 return false; 668 } 669 670 671 672 private boolean isSimple(ElementDefinition e) { 673 //we can tell if e is a primitive because it has types 674 if (e.getType().isEmpty()) { 675 return false; 676 } 677 if (e.getType().size() == 1 && isBase(e.getType().get(0).getWorkingCode())) { 678 return false; 679 } 680 if (e.getType().size() > 1) { 681 return true; 682 } 683 StructureDefinition sd = context.getWorker().fetchTypeDefinition(e.getTypeFirstRep().getCode()); 684 if (sd != null) { 685 if (sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE) { 686 return true; 687 } 688 if (sd.getKind() == StructureDefinitionKind.COMPLEXTYPE) { 689 if (Utilities.existsInList(e.getTypeFirstRep().getCode(), "Extension", "CodeableConcept", "Coding", "Annotation", "Identifier", "HumanName", "SampledData", 690 "Address", "ContactPoint", "ContactDetail", "Timing", "Range", "Quantity", "Ratio", "Period", "Reference")) { 691 return true; 692 } 693 } 694 } 695 return false; 696 } 697 698 private boolean isBase(String code) { 699 return code != null && (code.equals("Element") || code.equals("BackboneElement")); 700 } 701 702 private List<ElementDefinition> getChildrenForPath(StructureDefinition profile, List<ElementDefinition> elements, String path) throws DefinitionException { 703 // do we need to do a name reference substitution? 704 for (ElementDefinition e : elements) { 705 if (e.getPath().equals(path) && e.hasContentReference()) { 706 String ref = e.getContentReference(); 707 ElementDefinition t = null; 708 // now, resolve the name 709 for (ElementDefinition e1 : elements) { 710 if (ref.equals("#"+e1.getId())) 711 t = e1; 712 } 713 if (t == null) 714 throw new DefinitionException("Unable to resolve content reference "+ref+" trying to resolve "+path); 715 path = t.getPath(); 716 break; 717 } 718 } 719 720 ElementDefinition t = null; 721 List<ElementDefinition> results = new ArrayList<ElementDefinition>(); 722 for (ElementDefinition e : elements) { 723 if (e.getPath().equals(path)) { 724 t = e; 725 } 726 if (e.getPath().startsWith(path+".") && !e.getPath().substring(path.length()+1).contains(".")) 727 results.add(e); 728 } 729 if (results.isEmpty() && t != null && t.getType().size() == 1) { 730 StructureDefinition tsd = context.getWorker().fetchTypeDefinition(t.getTypeFirstRep().getWorkingCode()); 731 return getChildrenForPath(tsd, tsd.getSnapshot().getElement(), tsd.getType()); 732 } 733 return results; 734 } 735 736 737 private boolean generateByProfile(StructureDefinition profile, boolean showCodeDetails) { 738 XhtmlNode x = new XhtmlNode(NodeType.Element, "div"); 739 if(context.isAddGeneratedNarrativeHeader()) { 740 x.para().b().tx("Generated Narrative: "+profile.present()+(showCodeDetails ? " with Details" : "")); 741 } 742 try { 743 generateByProfile(rcontext.getResource(), profile, rcontext.getResource(), profile.getSnapshot().getElement(), profile.getSnapshot().getElement().get(0), getChildrenForPath(profile, profile.getSnapshot().getElement(), rcontext.getResource().getResourceType().toString()), x, rcontext.getResource().getResourceType().toString(), showCodeDetails); 744 } catch (Exception e) { 745 e.printStackTrace(); 746 x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage()); 747 } 748 inject((DomainResource) rcontext.getResource(), x, NarrativeStatus.GENERATED); 749 return true; 750 } 751 752 private void generateByProfile(Resource res, StructureDefinition profile, Base e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome { 753 generateByProfile(new ResourceWrapperDirect(this.context, res), profile, new BaseWrapperDirect(this.context, e), allElements, defn, children, x, path, showCodeDetails, 0); 754 } 755 756 private void generateByProfile(ResourceWrapper res, StructureDefinition profile, BaseWrapper e, List<ElementDefinition> allElements, ElementDefinition defn, List<ElementDefinition> children, XhtmlNode x, String path, boolean showCodeDetails, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome { 757 if (children.isEmpty()) { 758 renderLeaf(res, e, defn, x, x, false, showCodeDetails, readDisplayHints(defn), path, indent); 759 } else { 760 List<PropertyWrapper> pl = splitExtensions(profile, e.children()); 761 for (PropertyWrapper p : pl) { 762 generateForProperty(res, profile, allElements, children, x, path, showCodeDetails, indent, false, p); 763 } 764 for (PropertyWrapper p : pl) { 765 generateForProperty(res, profile, allElements, children, x, path, showCodeDetails, indent, true, p); 766 } 767 } 768 } 769 770 private void generateForProperty(ResourceWrapper res, StructureDefinition profile, 771 List<ElementDefinition> allElements, List<ElementDefinition> children, XhtmlNode x, String path, 772 boolean showCodeDetails, int indent, boolean round2, PropertyWrapper p) 773 throws UnsupportedEncodingException, IOException, EOperationOutcome { 774 if (p.hasValues()) { 775 ElementDefinition child = getElementDefinition(children, path+"."+p.getName(), p); 776 if (child == null) { 777 child = p.getElementDefinition(); 778 } 779 if (child != null) { 780 if (!child.getBase().hasPath() || !child.getBase().getPath().startsWith("Resource.")) { 781 generateElementByProfile(res, profile, allElements, x, path, showCodeDetails, indent, p, child, round2); 782 } 783 } 784 } 785 } 786 787 public void generateElementByProfile(ResourceWrapper res, StructureDefinition profile, List<ElementDefinition> allElements, XhtmlNode x, String path, 788 boolean showCodeDetails, int indent, PropertyWrapper p, ElementDefinition child, boolean round2) throws UnsupportedEncodingException, IOException, EOperationOutcome { 789 Map<String, String> displayHints = readDisplayHints(child); 790 if ("DomainResource.contained".equals(child.getBase().getPath())) { 791 if (round2) { 792 for (BaseWrapper v : p.getValues()) { 793 if (v.getBase() != null && !RendererFactory.hasSpecificRenderer(v.fhirType())) { 794 x.hr(); 795 RenderingContext ctxt = context.copy(); 796 ctxt.setContained(true); 797 ResourceRenderer rnd = RendererFactory.factory(v.fhirType(), ctxt); 798 ResourceWrapper rw = null; 799 if (v.getBase() instanceof org.hl7.fhir.r5.elementmodel.Element) { 800 rw = new ElementWrappers.ResourceWrapperMetaElement(ctxt, (org.hl7.fhir.r5.elementmodel.Element) v.getBase()); 801 } else if (v.getBase() instanceof Resource){ 802 rw = new DirectWrappers.ResourceWrapperDirect(ctxt, (Resource) v.getBase()); 803 } else { 804 throw new FHIRException("Not handled: base = "+v.getBase().getClass().getName()); 805 } 806 rnd.render(x.blockquote(), rw); 807 } 808 } 809 } 810 } else if (!round2 && !exemptFromRendering(child)) { 811 if (isExtension(p)) { 812 hasExtensions = true; 813 } 814 List<ElementDefinition> grandChildren = getChildrenForPath(profile, allElements, path+"."+p.getName()); 815 filterGrandChildren(grandChildren, path+"."+p.getName(), p); 816 if (p.getValues().size() > 0) { 817 if (isSimple(child)) { 818 XhtmlNode para = x.isPara() ? para = x : x.para(); 819 String name = p.getName(); 820 if (name.endsWith("[x]")) 821 name = name.substring(0, name.length() - 3); 822 if (showCodeDetails || !isDefaultValue(displayHints, p.getValues())) { 823 para.b().addText(name); 824 para.tx(": "); 825 if (renderAsList(child) && p.getValues().size() > 1) { 826 XhtmlNode list = x.ul(); 827 for (BaseWrapper v : p.getValues()) 828 renderLeaf(res, v, child, x, list.li(), false, showCodeDetails, displayHints, path, indent); 829 } else { 830 boolean first = true; 831 for (BaseWrapper v : p.getValues()) { 832 if (first) { 833 first = false; 834 } else { 835 para.tx(", "); 836 } 837 renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent); 838 } 839 } 840 } 841 } else if (canDoTable(path, p, grandChildren, x)) { 842 XhtmlNode xn = new XhtmlNode(NodeType.Element, getHeader()); 843 xn.addText(Utilities.capitalize(Utilities.camelCase(Utilities.pluralizeMe(p.getName())))); 844 XhtmlNode tbl = new XhtmlNode(NodeType.Element, "table"); 845 tbl.setAttribute("class", "grid"); 846 XhtmlNode tr = tbl.tr(); 847 tr.td().tx("-"); // work around problem with empty table rows 848 boolean add = addColumnHeadings(tr, grandChildren); 849 for (BaseWrapper v : p.getValues()) { 850 if (v != null) { 851 tr = tbl.tr(); 852 tr.td().tx("*"); // work around problem with empty table rows 853 add = addColumnValues(res, tr, grandChildren, v, showCodeDetails, displayHints, path, indent) || add; 854 } 855 } 856 if (add) { 857 x.add(xn); 858 x.add(tbl); 859 } 860 } else if (isExtension(p)) { 861 for (BaseWrapper v : p.getValues()) { 862 if (v != null) { 863 PropertyWrapper vp = v.getChildByName("value"); 864 PropertyWrapper ev = v.getChildByName("extension"); 865 if (vp.hasValues()) { 866 BaseWrapper vv = vp.value(); 867 XhtmlNode para = x.para(); 868 para.b().addText(p.getStructure().present()); 869 para.tx(": "); 870 renderLeaf(res, vv, child, x, para, false, showCodeDetails, displayHints, path, indent); 871 } else if (ev.hasValues()) { 872 XhtmlNode bq = x.addTag("blockquote"); 873 bq.para().b().addText(isExtension(p) ? p.getStructure().present() : p.getName()); 874 for (BaseWrapper vv : ev.getValues()) { 875 StructureDefinition ex = context.getWorker().fetchTypeDefinition("Extension"); 876 List<ElementDefinition> children = getChildrenForPath(profile, ex.getSnapshot().getElement(), "Extension"); 877 generateByProfile(res, ex, vv, allElements, child, children, bq, "Extension", showCodeDetails, indent+1); 878 } 879 } 880 } 881 } 882 } else { 883 for (BaseWrapper v : p.getValues()) { 884 if (v != null) { 885 XhtmlNode bq = x.addTag("blockquote"); 886 bq.para().b().addText(isExtension(p) ? p.getStructure().present() : p.getName()); 887 generateByProfile(res, profile, v, allElements, child, grandChildren, bq, path+"."+p.getName(), showCodeDetails, indent+1); 888 } 889 } 890 } 891 } 892 } 893 } 894 895 896 private String getHeader() { 897 int i = 3; 898 while (i <= context.getHeaderLevelContext()) 899 i++; 900 if (i > 6) 901 i = 6; 902 return "h"+Integer.toString(i); 903 } 904 905 private List<PropertyWrapper> getValues(String path, PropertyWrapper p, ElementDefinition e) { 906 List<PropertyWrapper> res = new ArrayList<PropertyWrapper>(); 907 for (BaseWrapper v : p.getValues()) { 908 for (PropertyWrapper g : v.children()) { 909 if ((path+"."+p.getName()+"."+g.getName()).equals(e.getPath())) 910 res.add(p); 911 } 912 } 913 return res; 914 } 915 916 private boolean canDoTable(String path, PropertyWrapper p, List<ElementDefinition> grandChildren, XhtmlNode x) { 917 if (isExtension(p)) { 918 return false; 919 } 920 if (x.getName().equals("p")) { 921 return false; 922 } 923 924 for (ElementDefinition e : grandChildren) { 925 List<PropertyWrapper> values = getValues(path, p, e); 926 if (values.size() > 1 || !isSimple(e) || !canCollapse(e)) 927 return false; 928 } 929 return true; 930 } 931 932 public boolean isExtension(PropertyWrapper p) { 933 return p.getName().contains("extension["); 934 } 935 936 937 private boolean canCollapse(ElementDefinition e) { 938 // we can collapse any data type 939 return !e.getType().isEmpty(); 940 } 941 private boolean exemptFromRendering(ElementDefinition child) { 942 if (child == null) 943 return false; 944 if ("Composition.subject".equals(child.getPath())) 945 return true; 946 if ("Composition.section".equals(child.getPath())) 947 return true; 948 return false; 949 } 950 951 private boolean renderAsList(ElementDefinition child) { 952 if (child.getType().size() == 1) { 953 String t = child.getType().get(0).getWorkingCode(); 954 if (t.equals("Address") || t.equals("Reference")) 955 return true; 956 } 957 return false; 958 } 959 960 private boolean addColumnHeadings(XhtmlNode tr, List<ElementDefinition> grandChildren) { 961 boolean b = false; 962 for (ElementDefinition e : grandChildren) { 963 b = true; 964 tr.td().b().addText(Utilities.capitalize(tail(e.getPath()))); 965 } 966 return b; 967 } 968 969 private boolean addColumnValues(ResourceWrapper res, XhtmlNode tr, List<ElementDefinition> grandChildren, BaseWrapper v, boolean showCodeDetails, Map<String, String> displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome { 970 boolean b = false; 971 for (ElementDefinition e : grandChildren) { 972 PropertyWrapper p = v.getChildByName(e.getPath().substring(e.getPath().lastIndexOf(".")+1)); 973 XhtmlNode td = tr.td(); 974 if (p == null || p.getValues().size() == 0 || p.getValues().get(0) == null) { 975 b = true; 976 td.tx(" "); 977 } else { 978 for (BaseWrapper vv : p.getValues()) { 979 b = true; 980 td.sep(", "); 981 renderLeaf(res, vv, e, td, td, false, showCodeDetails, displayHints, path, indent); 982 } 983 } 984 } 985 return b; 986 } 987 988 private void filterGrandChildren(List<ElementDefinition> grandChildren, String string, PropertyWrapper prop) { 989 List<ElementDefinition> toRemove = new ArrayList<ElementDefinition>(); 990 toRemove.addAll(grandChildren); 991 for (BaseWrapper b : prop.getValues()) { 992 List<ElementDefinition> list = new ArrayList<ElementDefinition>(); 993 for (ElementDefinition ed : toRemove) { 994 PropertyWrapper p = b.getChildByName(tail(ed.getPath())); 995 if (p != null && p.hasValues()) 996 list.add(ed); 997 } 998 toRemove.removeAll(list); 999 } 1000 grandChildren.removeAll(toRemove); 1001 } 1002 1003 private List<PropertyWrapper> splitExtensions(StructureDefinition profile, List<PropertyWrapper> children) throws UnsupportedEncodingException, IOException, FHIRException { 1004 List<PropertyWrapper> results = new ArrayList<PropertyWrapper>(); 1005 Map<String, PropertyWrapper> map = new HashMap<String, PropertyWrapper>(); 1006 for (PropertyWrapper p : children) 1007 if (p.getName().equals("extension") || p.getName().equals("modifierExtension")) { 1008 // we're going to split these up, and create a property for each url 1009 if (p.hasValues()) { 1010 for (BaseWrapper v : p.getValues()) { 1011 Base b = v.getBase(); 1012 if (!(b instanceof Extension)) { 1013 throw new FHIRException("huh?"); 1014 } 1015 Extension ex = (Extension) b; 1016 String url = ex.getUrl(); 1017 StructureDefinition ed = getContext().getWorker().fetchResource(StructureDefinition.class, url); 1018 if (ed == null) { 1019 if (xverManager == null) { 1020 xverManager = new XVerExtensionManager(context.getWorker()); 1021 } 1022 if (xverManager.matchingUrl(url) && xverManager.status(url) == XVerExtensionStatus.Valid) { 1023 ed = xverManager.makeDefinition(url); 1024 new ContextUtilities(getContext().getWorker()).generateSnapshot(ed); 1025 getContext().getWorker().cacheResource(ed); 1026 } 1027 } 1028 if (p.getName().equals("modifierExtension") && ed == null) { 1029 throw new DefinitionException("Unknown modifier extension "+url); 1030 } 1031 PropertyWrapper pe = map.get(p.getName()+"["+url+"]"); 1032 if (pe == null) { 1033 if (ed == null) { 1034 if (url != null && url.startsWith("http://hl7.org/fhir") && !url.startsWith("http://hl7.org/fhir/us")) { 1035 throw new DefinitionException("unknown extension "+url); 1036 } 1037 // System.out.println("unknown extension "+url); 1038 pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", p.getTypeCode(), p.getDefinition(), p.getMinCardinality(), p.getMaxCardinality(), ex), null); 1039 } else { 1040 ElementDefinition def = ed.getSnapshot().getElement().get(0); 1041 pe = new PropertyWrapperDirect(this.context, new Property(p.getName()+"["+url+"]", "Extension", def.getDefinition(), def.getMin(), def.getMax().equals("*") ? Integer.MAX_VALUE : Integer.parseInt(def.getMax()), ex), ed.getSnapshot().getElementFirstRep()); 1042 ((PropertyWrapperDirect) pe).getWrapped().setStructure(ed); 1043 } 1044 results.add(pe); 1045 } else 1046 pe.getValues().add(v); 1047 } 1048 } 1049 } else 1050 results.add(p); 1051 return results; 1052 } 1053 1054 1055 private Map<String, String> readDisplayHints(ElementDefinition defn) throws DefinitionException { 1056 Map<String, String> hints = new HashMap<String, String>(); 1057 if (defn != null) { 1058 String displayHint = ToolingExtensions.getDisplayHint(defn); 1059 if (!Utilities.noString(displayHint)) { 1060 String[] list = displayHint.split(";"); 1061 for (String item : list) { 1062 String[] parts = item.split(":"); 1063 if (parts.length == 1) { 1064 hints.put("value", parts[0].trim()); 1065 } else { 1066 if (parts.length != 2) { 1067 throw new DefinitionException("error reading display hint: '"+displayHint+"'"); 1068 } 1069 hints.put(parts[0].trim(), parts[1].trim()); 1070 } 1071 } 1072 } 1073 } 1074 return hints; 1075 } 1076 1077 @SuppressWarnings("rawtypes") 1078 private boolean isDefaultValue(Map<String, String> displayHints, List<BaseWrapper> list) throws UnsupportedEncodingException, IOException, FHIRException { 1079 if (list.size() != 1) 1080 return false; 1081 if (list.get(0).getBase() instanceof PrimitiveType) 1082 return isDefault(displayHints, (PrimitiveType) list.get(0).getBase()); 1083 else 1084 return false; 1085 } 1086 1087 private boolean isDefault(Map<String, String> displayHints, PrimitiveType primitiveType) { 1088 String v = primitiveType.asStringValue(); 1089 if (!Utilities.noString(v) && displayHints.containsKey("default") && v.equals(displayHints.get("default"))) 1090 return true; 1091 return false; 1092 } 1093 1094 1095 protected String tail(String path) { 1096 return path.substring(path.lastIndexOf(".")+1); 1097 } 1098 1099 public boolean canRender(Resource resource) { 1100 return context.getWorker().getResourceNames().contains(resource.fhirType()); 1101 } 1102 1103 public RendererType getRendererType() { 1104 return RendererType.PROFILE; 1105 } 1106 1107}