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