001package org.hl7.fhir.r5.renderers.utils; 002 003import java.util.Collection; 004import java.util.Locale; 005 006import org.hl7.fhir.r5.model.Base; 007import org.hl7.fhir.r5.model.DomainResource; 008import org.hl7.fhir.r5.model.ElementDefinition; 009import org.hl7.fhir.r5.model.Enumeration; 010import org.hl7.fhir.r5.model.Narrative; 011import org.hl7.fhir.r5.model.Property; 012import org.hl7.fhir.r5.model.Resource; 013import org.hl7.fhir.utilities.xhtml.NodeType; 014import org.hl7.fhir.utilities.xhtml.XhtmlNode; 015 016/** 017 * This class is used to walk through the resources when rendering, whether 018 * the resource is a native resource or loaded by the element model 019 */ 020public class ResourceWrapperNative extends ResourceWrapper { 021 022 protected Base element; 023 024 ResourceWrapperNative() { 025 super(); 026 } 027 028 private ResourceWrapper makeChild(String name, int index, ElementKind kind, Base element) { 029 ResourceWrapperNative self = new ResourceWrapperNative(); 030 self.contextUtils = this.contextUtils; 031 self.parent = this; 032 self.name = name; 033 self.index = index; 034 self.kind = kind; 035 self.element = element; 036 return self; 037 } 038 039 public String fhirVersion() { 040 return element.getFHIRPublicationVersion().toCode(); 041 } 042 043 public String fhirType() { 044 if (kind == ElementKind.BackboneElement) { 045 return basePath(); 046 } else { 047 return element.fhirType(); 048 } 049 } 050 051 public boolean isPrimitive() { 052 return element.isPrimitive(); 053 } 054 055 public boolean hasPrimitiveValue() { 056 return element.hasPrimitiveValue(); 057 } 058 059 public String primitiveValue() { 060 return element.primitiveValue(); 061 } 062 063 protected void loadTheChildren() { 064 065 for (Property p : element.children()) { 066 String name = p.getName(); 067 int i = 0; 068 for (Base v : p.getValues()) { 069 loadElementChild(p, name, i, v); 070 i++; 071 } 072 } 073 } 074 075 private void loadElementChild(Property p, String name, int i, Base v) { 076 ElementKind kind = determineModelKind(p, v); 077 int index = p.isList() ? i : -1; 078 ElementDefinition ed = null; 079 children.add(makeChild(name, index, kind, v)); 080 } 081 082 private ElementKind determineModelKind(Property p, Base v) { 083 if (v.isPrimitive()) { 084 return ElementKind.PrimitiveType; 085 } else if (contextUtils.isDatatype(v.fhirType())) { 086 return ElementKind.DataType; 087 } else if (!v.isResource()) { 088 return ElementKind.BackboneElement; 089 } else if ("Bundle.entry".equals(fhirType()) && "resource".equals(p.getName())) { 090 return ElementKind.BundleEntry; 091 } else if ("Bundle".equals(fhirType()) && "outcome".equals(p.getName())) { 092 return ElementKind.InlineResource; 093 } else if ("Bundle".equals(fhirType()) && "issues".equals(p.getName())) { 094 return ElementKind.InlineResource; 095 } else if (isResource() && "contained".equals(p.getName())) { 096 return ElementKind.ContainedResource; 097 } else { 098 return ElementKind.InlineResource; 099 } 100 } 101 102 public boolean isResource() { 103 return element.isResource(); 104 } 105 106 public boolean canHaveNarrative() { 107 if (!isResource()) { 108 return false; 109 } 110 return element instanceof DomainResource; 111 } 112 113 public XhtmlNode getNarrative() { 114 if (!canHaveNarrative()) { 115 return null; 116 } 117 ResourceWrapper text = child("text"); 118 if (text == null) { 119 return null; 120 } 121 ResourceWrapper div = text.child("div"); 122 if (div == null) { 123 return null; 124 } 125 return ((ResourceWrapperNative) div).element.getXhtml(); 126 } 127 128 public boolean hasNarrative() { 129 if (!canHaveNarrative()) { 130 return false; 131 } 132 ResourceWrapper text = child("text"); 133 if (text == null) { 134 return false; 135 } 136 ResourceWrapper div = text.child("div"); 137 if (div == null) { 138 return false; 139 } 140 return ((ResourceWrapperNative) div).element.getXhtml() != null; 141 } 142 143 public void setNarrative(XhtmlNode x, String status, boolean multiLangMode, Locale locale, boolean isPretty) { 144 if (element instanceof DomainResource) { 145 DomainResource r = (DomainResource) element; 146 r.getText().setUserData("renderer.generated", true); 147 if (!r.hasText() || !r.getText().hasDiv()) { 148 r.setText(new Narrative()); 149 r.getText().setStatusAsString(status); 150 } 151 if (multiLangMode) { 152 if (!r.getText().hasDiv()) { 153 XhtmlNode div = new XhtmlNode(NodeType.Element, "div"); 154 div.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); 155 r.getText().setDiv(div); 156 } else { 157 r.getText().getDiv().getChildNodes().removeIf(c -> !"div".equals(c.getName()) || !c.hasAttribute("xml:lang")); 158 } 159 markLanguage(x, locale); 160 r.getText().getDiv().addChildNode(x); 161 } else { 162 if (!x.hasAttribute("xmlns")) 163 x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); 164 if (r.hasLanguage()) { 165 // use both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues 166 x.setAttribute("lang", r.getLanguage()); 167 x.setAttribute("xml:lang", r.getLanguage()); 168 } 169 r.getText().setDiv(x); 170 } 171 } else { 172 throw new Error("Cannot call setNarrative on a "+element.fhirType()); 173 } 174 } 175 176 public void markLanguage(XhtmlNode x, Locale locale) { 177 x.setAttribute("lang", locale.toString()); 178 x.setAttribute("xml:lang", locale.toString()); 179 x.addTag(0, "hr"); 180 x.addTag(0, "p").b().tx(locale.getDisplayName()); 181 x.addTag(0, "hr"); 182 } 183 184 185 public String getId() { 186 return element.getIdBase(); 187 } 188 189 @Override 190 public String toString() { 191 return name + (index == -1 ? "" : "["+index+"]")+": "+fhirType()+" ("+kind+"/"+path()+"): native = "+element.fhirType()+" -> "+element.toString(); 192 } 193 194 public Resource getResourceNative() { 195 ResourceWrapper focus = getResourceWrapper(); 196 return focus == null ? null : (Resource) ((ResourceWrapperNative) focus).element; 197 } 198 199 public boolean hasFormatComment() { 200 return element.hasFormatComment(); 201 } 202 203 public Collection<String> getFormatCommentsPre() { 204 return element.getFormatCommentsPre(); 205 } 206 207 public XhtmlNode getXhtml() { 208 return element.getXhtml(); 209 } 210 211 public Base getBase() { 212 return element; 213 } 214 215 public boolean isDirect() { 216 return true; 217 } 218 219 public String getWebPath() { 220 if (isResource()) { 221 return ((Resource) element).getWebPath(); 222 } else { 223 return null; 224 } 225 } 226 227 public String getCodeSystemUri() { 228 if (element instanceof Enumeration<?>) { 229 return ((Enumeration<?>) element).getSystem(); 230 } 231 return null; 232 } 233 234}