
001package org.hl7.fhir.r5.renderers.utils; 002 003import java.io.IOException; 004import java.util.Collection; 005import java.util.Locale; 006 007import org.hl7.fhir.r5.elementmodel.Element; 008import org.hl7.fhir.r5.model.Base; 009import org.hl7.fhir.r5.model.ElementDefinition; 010import org.hl7.fhir.r5.model.Resource; 011import org.hl7.fhir.r5.model.ValueSet; 012import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 013import org.hl7.fhir.utilities.Utilities; 014import org.hl7.fhir.utilities.xhtml.NodeType; 015import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 016import org.hl7.fhir.utilities.xhtml.XhtmlNode; 017 018/** 019 * This class is used to walk through the resources when rendering, whether 020 * the resource is a native resource or loaded by the element model 021 */ 022@MarkedToMoveToAdjunctPackage 023public class ResourceWrapperModel extends ResourceWrapper { 024 025 protected Element model; 026 027 ResourceWrapperModel() { 028 super(); 029 } 030 031 private ResourceWrapperModel makeChild(String name, int index, ElementKind kind, Element em) { 032 ResourceWrapperModel self = new ResourceWrapperModel(); 033 self.contextUtils = this.contextUtils; 034 self.parent = this; 035 self.name = name; 036 self.index = index; 037 self.kind = kind; 038 self.model = em; 039 return self; 040 } 041 042 public String fhirVersion() { 043 return model.getFHIRPublicationVersion().toCode(); 044 } 045 046 public String fhirType() { 047 if (kind == ElementKind.BackboneElement) { 048 return basePath(); 049 } else { 050 return model.fhirType(); 051 } 052 } 053 054 public boolean isPrimitive() { 055 return model.isPrimitive(); 056 } 057 058 public boolean hasPrimitiveValue() { 059 return model.hasPrimitiveValue(); 060 } 061 062 public String primitiveValue() { 063 return model.primitiveValue(); 064 } 065 066 protected void loadTheChildren() { 067 for (Element child : model.getChildren()) { 068 String name = child.getProperty().isChoice() ? child.getProperty().getName() : child.getName(); 069 int index = child.isList() ? child.getIndex() : -1; 070 ElementKind kind = determineModelKind(child); 071 children.add(makeChild(name, index, kind, child)); 072 } 073 } 074 075 private ElementKind determineModelKind(Element child) { 076 if (child.isPrimitive()) { 077 return ElementKind.PrimitiveType; 078 } else if (child.fhirType().contains("Backbone")) { 079 return ElementKind.BackboneElement; 080 } else if (child.getProperty().getContextUtils().isDatatype(child.fhirType())) { 081 return ElementKind.DataType; 082 } else if (!child.isResource()) { 083 return ElementKind.BackboneElement; 084 } else { 085 switch (child.getSpecial()) { 086 case BUNDLE_ENTRY: 087 return ElementKind.BundleEntry; 088 case BUNDLE_ISSUES: 089 return ElementKind.InlineResource; 090 case BUNDLE_OUTCOME: 091 return ElementKind.InlineResource; 092 case CONTAINED: 093 return ElementKind.ContainedResource; 094 case PARAMETER: 095 return ElementKind.InlineResource; 096 default: 097 return ElementKind.IndependentResource; 098 } 099 } 100 } 101 102 103 public boolean isResource() { 104 return model.isResource(); 105 } 106 107 public boolean canHaveNarrative() { 108 if (!isResource()) { 109 return false; 110 } 111 return contextUtils.isDomainResource(fhirType()); 112 } 113 114 public XhtmlNode getNarrative() { 115 if (!canHaveNarrative()) { 116 return null; 117 } 118 ResourceWrapper text = child("text"); 119 if (text == null) { 120 return null; 121 } 122 ResourceWrapper div = text.child("div"); 123 if (div == null) { 124 return null; 125 } 126 return ((ResourceWrapperModel) div).model.getXhtml(); 127 } 128 129 public boolean hasNarrative() { 130 if (!canHaveNarrative()) { 131 return false; 132 } 133 ResourceWrapper text = child("text"); 134 if (text == null) { 135 return false; 136 } 137 ResourceWrapper div = text.child("div"); 138 if (div == null) { 139 return false; 140 } 141 return ((ResourceWrapperModel) div).model.getXhtml() != null; 142 } 143 144 @Override 145 public void setNarrative(XhtmlNode x, String status, boolean multiLangMode, Locale locale, boolean isPretty) throws IOException { 146 org.hl7.fhir.r5.elementmodel.Element txt = model.getNamedChild("text"); 147 if (txt == null) { 148 txt = new org.hl7.fhir.r5.elementmodel.Element("text", model.getProperty().getChild(null, "text")); 149 int i = 0; 150 while (i < model.getChildren().size() && (model.getChildren().get(i).getName().equals("id") || model.getChildren().get(i).getName().equals("meta") || model.getChildren().get(i).getName().equals("implicitRules") || model.getChildren().get(i).getName().equals("language"))) { 151 i++; 152 } 153 if (i >= model.getChildren().size()) 154 model.getChildren().add(txt); 155 else 156 model.getChildren().add(i, txt); 157 } 158 org.hl7.fhir.r5.elementmodel.Element st = txt.getNamedChild("status"); 159 if (st == null) { 160 st = new org.hl7.fhir.r5.elementmodel.Element("status", txt.getProperty().getChild(null, "status")); 161 txt.getChildren().add(0, st); 162 } 163 st.setValue(status); 164 org.hl7.fhir.r5.elementmodel.Element div = txt.getNamedChild("div"); 165 if (div == null) { 166 div = new org.hl7.fhir.r5.elementmodel.Element("div", txt.getProperty().getChild(null, "div")); 167 txt.getChildren().add(div); 168 } 169 // now process the xhtml 170 if (multiLangMode) { 171 XhtmlNode xd = div.getXhtml(); 172 if (xd == null) { 173 xd = new XhtmlNode(NodeType.Element, "div"); 174 xd.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); 175 div.setXhtml(xd); 176 } else { 177 xd.getChildNodes().removeIf(c -> !"div".equals(c.getName()) || !c.hasAttribute("xml:lang")); 178 } 179 markLanguage(x, locale); 180 xd.addChildNode(x); 181 } else { 182 if (!x.hasAttribute("xmlns")) { 183 x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); 184 } 185 String l = model.getChildValue("language"); 186 if (!Utilities.noString(l)) { 187 // use both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues 188 x.setAttribute("lang", l); 189 x.setAttribute("xml:lang", l); 190 } 191 div.setXhtml(x); 192 } 193 div.setValue(new XhtmlComposer(XhtmlComposer.XML, isPretty).compose(div.getXhtml())); 194 } 195 196 public void markLanguage(XhtmlNode x, Locale locale) { 197 x.setAttribute("lang", locale.toLanguageTag()); 198 x.setAttribute("xml:lang", locale.toLanguageTag()); 199 x.addTag(0, "hr"); 200 x.addTag(0, "p").b().tx(locale.getDisplayName()); 201 x.addTag(0, "hr"); 202 } 203 204 205 public String getId() { 206 return model.getIdBase(); 207 } 208 public boolean hasId() { 209 return model.getIdBase() != null; 210 } 211 212 public ResourceWrapper setId(String id) { 213 model.setIdBase(id); 214 return this; 215 } 216 217 @Override 218 public String toString() { 219 return name + (index == -1 ? "" : "["+index+"]")+": "+fhirType()+" ("+kind+"/"+path()+"): element = "+model.fhirType()+" -> "+model.toString(); 220 } 221 222 public boolean matches(ResourceWrapperModel b) { 223 if (isEmpty() || b.isEmpty()) { 224 return isEmpty() && b.isEmpty(); 225 } else { 226 if (hasPrimitiveValue() || b.hasPrimitiveValue()) { 227 if (!hasPrimitiveValue() || !b.hasPrimitiveValue() || !primitiveValue().equals(b.primitiveValue())) { 228 return false; 229 } 230 } 231 if (children().size() != b.children().size()) { 232 return false; 233 } else { 234 for (int i = 0; i < children().size(); i++) { 235 if (!children().get(i).matches(b.children().get(i))) { 236 return false; 237 } 238 } 239 return true; 240 } 241 } 242 } 243 244 public Resource getResourceNative() { 245 return null; 246 } 247 248 public boolean hasFormatComment() { 249 return model.hasFormatComment(); 250 } 251 252 public Collection<String> getFormatCommentsPre() { 253 return model.getFormatCommentsPre(); 254 } 255 256 public XhtmlNode getXhtml() { 257 return model.getXhtml(); 258 } 259 260 public Base getBase() { 261 return model; 262 } 263 264 public boolean isDirect() { 265 return false; 266 } 267 268 public String getWebPath() { 269 return model.getWebPath(); 270 } 271 272 public String getCodeSystemUri() { 273 ElementDefinition pd = model.getProperty().getDefinition(); 274 if (pd != null && pd.hasBinding() && pd.getBinding().hasValueSet()) { 275 ValueSet vs = contextUtils.getWorker().fetchResource(ValueSet.class, pd.getBinding().getValueSet()); 276 if (vs != null && vs.hasCompose() && !vs.getCompose().hasExclude() && vs.getCompose().getInclude().size() == 1) { 277 return vs.getCompose().getIncludeFirstRep().getSystem(); 278 } 279 } 280 return null; 281 } 282 283 @Override 284 public boolean hasUserData(String name) { 285 return model.hasUserData(name); 286 } 287 288 @Override 289 public Object getUserData(String name) { 290 return model.getUserData(name); 291 } 292 293 294}