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