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}