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