
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 209 @Override 210 public String toString() { 211 return name + (index == -1 ? "" : "["+index+"]")+": "+fhirType()+" ("+kind+"/"+path()+"): element = "+model.fhirType()+" -> "+model.toString(); 212 } 213 214 public boolean matches(ResourceWrapperModel b) { 215 if (isEmpty() || b.isEmpty()) { 216 return isEmpty() && b.isEmpty(); 217 } else { 218 if (hasPrimitiveValue() || b.hasPrimitiveValue()) { 219 if (!hasPrimitiveValue() || !b.hasPrimitiveValue() || !primitiveValue().equals(b.primitiveValue())) { 220 return false; 221 } 222 } 223 if (children().size() != b.children().size()) { 224 return false; 225 } else { 226 for (int i = 0; i < children().size(); i++) { 227 if (!children().get(i).matches(b.children().get(i))) { 228 return false; 229 } 230 } 231 return true; 232 } 233 } 234 } 235 236 public Resource getResourceNative() { 237 return null; 238 } 239 240 public boolean hasFormatComment() { 241 return model.hasFormatComment(); 242 } 243 244 public Collection<String> getFormatCommentsPre() { 245 return model.getFormatCommentsPre(); 246 } 247 248 public XhtmlNode getXhtml() { 249 return model.getXhtml(); 250 } 251 252 public Base getBase() { 253 return model; 254 } 255 256 public boolean isDirect() { 257 return false; 258 } 259 260 public String getWebPath() { 261 return model.getWebPath(); 262 } 263 264 public String getCodeSystemUri() { 265 ElementDefinition pd = model.getProperty().getDefinition(); 266 if (pd != null && pd.hasBinding() && pd.getBinding().hasValueSet()) { 267 ValueSet vs = contextUtils.getWorker().fetchResource(ValueSet.class, pd.getBinding().getValueSet()); 268 if (vs != null && vs.hasCompose() && !vs.getCompose().hasExclude() && vs.getCompose().getInclude().size() == 1) { 269 return vs.getCompose().getIncludeFirstRep().getSystem(); 270 } 271 } 272 return null; 273 } 274 275 @Override 276 public boolean hasUserData(String name) { 277 return model.hasUserData(name); 278 } 279 280 @Override 281 public Object getUserData(String name) { 282 return model.getUserData(name); 283 } 284 285 286}