
001package org.hl7.fhir.r5.renderers.utils; 002 003import java.io.IOException; 004import java.io.UnsupportedEncodingException; 005import java.nio.charset.StandardCharsets; 006import java.util.ArrayList; 007import java.util.List; 008 009import org.apache.commons.io.output.ByteArrayOutputStream; 010import org.hl7.fhir.exceptions.FHIRException; 011import org.hl7.fhir.exceptions.FHIRFormatError; 012import org.hl7.fhir.r5.elementmodel.Element; 013import org.hl7.fhir.r5.elementmodel.XmlParser; 014import org.hl7.fhir.r5.formats.IParser.OutputStyle; 015import org.hl7.fhir.r5.model.Base; 016import org.hl7.fhir.r5.model.ElementDefinition; 017import org.hl7.fhir.r5.model.Property; 018import org.hl7.fhir.r5.model.Resource; 019import org.hl7.fhir.r5.model.Narrative.NarrativeStatus; 020import org.hl7.fhir.r5.model.StringType; 021import org.hl7.fhir.r5.model.StructureDefinition; 022import org.hl7.fhir.r5.renderers.utils.BaseWrappers.BaseWrapper; 023import org.hl7.fhir.r5.renderers.utils.BaseWrappers.PropertyWrapper; 024import org.hl7.fhir.r5.renderers.utils.BaseWrappers.RendererWrapperImpl; 025import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper; 026import org.hl7.fhir.r5.renderers.utils.BaseWrappers.WrapperBaseImpl; 027import org.hl7.fhir.utilities.Utilities; 028import org.hl7.fhir.utilities.xhtml.XhtmlComposer; 029import org.hl7.fhir.utilities.xhtml.XhtmlNode; 030 031public class ElementWrappers { 032 033 public static class BaseWrapperMetaElement extends WrapperBaseImpl implements BaseWrapper { 034 private Element element; 035 private String type; 036 private StructureDefinition structure; 037 private ElementDefinition definition; 038 private List<ElementDefinition> children; 039 private List<PropertyWrapper> list; 040 041 public BaseWrapperMetaElement(RenderingContext context, Element element, String type, StructureDefinition structure, ElementDefinition definition) { 042 super(context); 043 this.element = element; 044 this.type = type; 045 this.structure = structure; 046 this.definition = definition; 047 } 048 049 @Override 050 public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException { 051 if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element")) 052 return null; 053 054 if (element.hasElementProperty()) { 055 return element; 056 } 057 ByteArrayOutputStream xml = new ByteArrayOutputStream(); 058 try { 059 new XmlParser(context.getWorker()).compose(element, xml, OutputStyle.PRETTY, null); 060 } catch (Exception e) { 061 throw new FHIRException(e.getMessage(), e); 062 } 063 if (context.getParser() == null) { 064 System.out.println("No version specific parser provided"); 065 } 066 if (context.getParser() == null) { 067 throw new Error("No type parser provided to renderer context"); 068 } else { 069 try { 070 return context.getParser().parseType(xml.toString(StandardCharsets.UTF_8), type); 071 } catch (Exception e) { 072 throw new FHIRException(e.getMessage(), e); 073 } 074 } 075 } 076 077 @Override 078 public List<PropertyWrapper> children() { 079 if (list == null) { 080 children = context.getProfileUtilities().getChildList(structure, definition, false, true); 081 if (children.isEmpty() && !Utilities.noString(type)) { 082 StructureDefinition sd = context.getWorker().fetchTypeDefinition(type); 083 children = context.getProfileUtilities().getChildList(sd, sd.getSnapshot().getElementFirstRep()); 084 } 085 list = new ArrayList<PropertyWrapper>(); 086 for (ElementDefinition child : children) { 087 List<Element> elements = new ArrayList<Element>(); 088 String name = tail(child.getPath()); 089 if (name.endsWith("[x]")) 090 element.getNamedChildrenWithWildcard(name, elements); 091 else 092 element.getNamedChildren(name, elements); 093 list.add(new PropertyWrapperMetaElement(context, structure, child, elements)); 094 } 095 } 096 return list; 097 } 098 099 @Override 100 public PropertyWrapper getChildByName(String name) { 101 for (PropertyWrapper p : children()) 102 if (p.getName().equals(name)) 103 return p; 104 return null; 105 } 106 107 @Override 108 public String fhirType() { 109 return element.fhirType(); 110 } 111 112 } 113 114 public static class ResourceWrapperMetaElement extends WrapperBaseImpl implements ResourceWrapper { 115 private Element wrapped; 116 private List<ResourceWrapper> list; 117 private List<PropertyWrapper> list2; 118 private StructureDefinition definition; 119 public ResourceWrapperMetaElement(RenderingContext context, Element wrapped) { 120 super(context); 121 this.wrapped = wrapped; 122 this.definition = wrapped.getProperty().getStructure(); 123 } 124 125 @Override 126 public List<ResourceWrapper> getContained() { 127 if (list == null) { 128 List<Element> children = wrapped.getChildrenByName("contained"); 129 list = new ArrayList<ResourceWrapper>(); 130 for (Element e : children) { 131 list.add(new ResourceWrapperMetaElement(context, e)); 132 } 133 } 134 return list; 135 } 136 137 @Override 138 public String getId() { 139 return wrapped.getNamedChildValue("id"); 140 } 141 142 @Override 143 public XhtmlNode getNarrative() throws FHIRFormatError, IOException, FHIRException { 144 Element txt = wrapped.getNamedChild("text"); 145 if (txt == null) 146 return null; 147 Element div = txt.getNamedChild("div"); 148 if (div == null) 149 return null; 150 else 151 return div.getXhtml(); 152 } 153 154 @Override 155 public String getName() { 156 return wrapped.getName(); 157 } 158 159 @Override 160 public String getNameFromResource() { 161 Property name = wrapped.getChildByName("name"); 162 if (name != null && name.hasValues()) { 163 Base b = name.getValues().get(0); 164 if (b.isPrimitive()) { 165 return b.primitiveValue(); 166 } else if (b.fhirType().equals("HumanName")) { 167 Property family = b.getChildByName("family"); 168 Property given = wrapped.getChildByName("given"); 169 String s = given != null && given.hasValues() ? given.getValues().get(0).primitiveValue() : ""; 170 if (family != null && family.hasValues() && family.getValues().get(0).primitiveValue() != null) 171 s = s + " " + family.getValues().get(0).primitiveValue().toUpperCase(); 172 return s; 173 } else { 174 // well, we couldn't get a name from that 175 return null; 176 } 177 } 178 return null; 179 } 180 181 @Override 182 public List<PropertyWrapper> children() { 183 if (list2 == null) { 184 List<ElementDefinition> children = context.getProfileUtilities().getChildList(definition, definition.getSnapshot().getElement().get(0)); 185 list2 = new ArrayList<PropertyWrapper>(); 186 for (ElementDefinition child : children) { 187 List<Element> elements = new ArrayList<Element>(); 188 if (child.getPath().endsWith("[x]")) 189 wrapped.getNamedChildrenWithWildcard(tail(child.getPath()), elements); 190 else 191 wrapped.getNamedChildren(tail(child.getPath()), elements); 192 list2.add(new PropertyWrapperMetaElement(context, definition, child, elements)); 193 } 194 } 195 return list2; 196 } 197 198 @Override 199 public void describe(XhtmlNode x) { 200 if (wrapped.hasChild("title") && wrapped.getChildValue("title") != null) { 201 x.tx(wrapped.getChildValue("title")); 202 } else if (wrapped.hasChild("name") && wrapped.getChildValue("name") != null) { 203 x.tx(wrapped.getChildValue("name")); 204 } else { 205 x.tx("?ngen-1?"); 206 } 207 } 208 209 @Override 210 public void injectNarrative(XhtmlNode x, NarrativeStatus status) throws IOException { 211 if (!x.hasAttribute("xmlns")) 212 x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); 213 String l = wrapped.getChildValue("language"); 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 org.hl7.fhir.r5.elementmodel.Element txt = wrapped.getNamedChild("text"); 220 if (txt == null) { 221 txt = new org.hl7.fhir.r5.elementmodel.Element("text", wrapped.getProperty().getChild(null, "text")); 222 int i = 0; 223 while (i < wrapped.getChildren().size() && (wrapped.getChildren().get(i).getName().equals("id") || wrapped.getChildren().get(i).getName().equals("meta") || wrapped.getChildren().get(i).getName().equals("implicitRules") || wrapped.getChildren().get(i).getName().equals("language"))) 224 i++; 225 if (i >= wrapped.getChildren().size()) 226 wrapped.getChildren().add(txt); 227 else 228 wrapped.getChildren().add(i, txt); 229 } 230 org.hl7.fhir.r5.elementmodel.Element st = txt.getNamedChild("status"); 231 if (st == null) { 232 st = new org.hl7.fhir.r5.elementmodel.Element("status", txt.getProperty().getChild(null, "status")); 233 txt.getChildren().add(0, st); 234 } 235 st.setValue(status.toCode()); 236 org.hl7.fhir.r5.elementmodel.Element div = txt.getNamedChild("div"); 237 if (div == null) { 238 div = new org.hl7.fhir.r5.elementmodel.Element("div", txt.getProperty().getChild(null, "div")); 239 txt.getChildren().add(div); 240 } 241 div.setValue(new XhtmlComposer(XhtmlComposer.XML, context.isPretty()).compose(x)); 242 div.setXhtml(x); 243 } 244 245 @Override 246 public BaseWrapper root() { 247 return new BaseWrapperMetaElement(context, wrapped, getName(), definition, definition.getSnapshot().getElementFirstRep()); 248 } 249 250 @Override 251 public StructureDefinition getDefinition() { 252 return definition; 253 } 254 255 @Override 256 public Base getBase() { 257 return wrapped; 258 } 259 260 @Override 261 public boolean hasNarrative() { 262 StructureDefinition sd = definition; 263 while (sd != null) { 264 if ("DomainResource".equals(sd.getType())) { 265 return true; 266 } 267 sd = context.getWorker().fetchResource(StructureDefinition.class, sd.getBaseDefinition(), sd); 268 } 269 return false; 270 } 271 272 @Override 273 public String fhirType() { 274 return wrapped.fhirType(); 275 } 276 277 @Override 278 public PropertyWrapper getChildByName(String name) { 279 for (PropertyWrapper p : children()) 280 if (p.getName().equals(name)) 281 return p; 282 return null; 283 } 284 285 public Element getElement() { 286 return wrapped; 287 } 288 289 @Override 290 public Resource getResource() { 291 return null; 292 } 293 294} 295 296 public static class PropertyWrapperMetaElement extends RendererWrapperImpl implements PropertyWrapper { 297 298 private StructureDefinition structure; 299 private ElementDefinition definition; 300 private List<Element> values; 301 private List<BaseWrapper> list; 302 303 public PropertyWrapperMetaElement(RenderingContext context, StructureDefinition structure, ElementDefinition definition, List<Element> values) { 304 super(context); 305 this.structure = structure; 306 this.definition = definition; 307 this.values = values; 308 } 309 310 @Override 311 public String getName() { 312 return tail(definition.getPath()); 313 } 314 315 @Override 316 public boolean hasValues() { 317 return values.size() > 0; 318 } 319 320 @Override 321 public List<BaseWrapper> getValues() { 322 if (list == null) { 323 list = new ArrayList<BaseWrapper>(); 324 for (Element e : values) { 325 list.add(new BaseWrapperMetaElement(context, e, e.fhirType(), structure, definition)); 326 } 327 } 328 return list; 329 } 330 331 @Override 332 public String getTypeCode() { 333 return definition.typeSummary(); 334 } 335 336 @Override 337 public String getDefinition() { 338 return definition.getDefinition(); 339 } 340 341 @Override 342 public int getMinCardinality() { 343 return definition.getMin(); 344 } 345 346 @Override 347 public int getMaxCardinality() { 348 return "*".equals(definition.getMax()) ? Integer.MAX_VALUE : Integer.valueOf(definition.getMax()); 349 } 350 351 @Override 352 public StructureDefinition getStructure() { 353 return structure; 354 } 355 356 @Override 357 public BaseWrapper value() { 358 if (getValues().size() != 1) 359 throw new Error("Access single value, but value count is "+getValues().size()); 360 return getValues().get(0); 361 } 362 363 @Override 364 public ResourceWrapper getAsResource() { 365 return new ElementWrappers.ResourceWrapperMetaElement(context, values.get(0)); 366 } 367 368 @Override 369 public String fhirType() { 370 return getTypeCode(); 371 } 372 373 @Override 374 public ElementDefinition getElementDefinition() { 375 return definition; 376 } 377 378 } 379 380}