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.r5.utils.UserDataNames;
014import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
015import org.hl7.fhir.utilities.xhtml.NodeType;
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 ResourceWrapperNative extends ResourceWrapper {
024
025  protected Base element;
026
027  ResourceWrapperNative() {
028    super();
029  }
030  
031  private ResourceWrapper makeChild(String name, int index, ElementKind kind, Base element) {
032    ResourceWrapperNative self = new ResourceWrapperNative();
033    self.contextUtils = this.contextUtils;
034    self.parent = this;
035    self.name = name;
036    self.index = index;
037    self.kind = kind;
038    self.element = element;
039    return self;
040  }
041
042  public String fhirVersion() {
043    return element.getFHIRPublicationVersion().toCode();
044  }
045
046  public String fhirType() {
047    if (kind == ElementKind.BackboneElement) {
048      return basePath();
049    } else {
050      return element.fhirType();
051    }
052  }
053
054  public boolean isPrimitive() {
055    return element.isPrimitive();
056  }
057
058  public boolean hasPrimitiveValue() {
059    return element.hasPrimitiveValue();
060  }
061
062  public String primitiveValue() {
063    return element.primitiveValue();
064  }
065
066  protected void loadTheChildren() {
067
068    for (Property p : element.children()) {
069      String name = p.getName();
070      int i = 0;
071      for (Base v : p.getValues()) {
072        loadElementChild(p, name, i, v);
073        i++;
074      }
075    }
076  }
077
078  private void loadElementChild(Property p, String name, int i, Base v) {
079    ElementKind kind = determineModelKind(p, v);      
080    int index = p.isList() ? i : -1;
081    ElementDefinition ed = null;
082    children.add(makeChild(name, index, kind, v));
083  }
084
085  private ElementKind determineModelKind(Property p, Base v) {
086    if (v.isPrimitive()) {
087      return ElementKind.PrimitiveType;
088    } else if (contextUtils.isDatatype(v.fhirType())) {
089      return ElementKind.DataType;
090    } else if (!v.isResource()) {
091      return ElementKind.BackboneElement;
092    } else if ("Bundle.entry".equals(fhirType()) && "resource".equals(p.getName())) {
093      return ElementKind.BundleEntry;
094    } else if ("Bundle".equals(fhirType()) && "outcome".equals(p.getName())) {
095      return ElementKind.InlineResource;
096    } else if ("Bundle".equals(fhirType()) && "issues".equals(p.getName())) {
097      return ElementKind.InlineResource;
098    } else if (isResource() && "contained".equals(p.getName())) {
099      return ElementKind.ContainedResource;
100    } else {
101      return ElementKind.InlineResource;
102    }
103  }
104
105  public boolean isResource() {
106    return element.isResource();
107  }
108
109  public boolean canHaveNarrative() {
110    if (!isResource()) {
111      return false;
112    }
113    return element instanceof DomainResource;
114  }
115
116  public XhtmlNode getNarrative() {
117    if (!canHaveNarrative()) {
118      return null;
119    }
120    ResourceWrapper text = child("text");
121    if (text == null) {
122      return null;
123    }
124    ResourceWrapper div = text.child("div");
125    if (div == null) {
126      return null;
127    }
128    return ((ResourceWrapperNative) div).element.getXhtml();
129  }
130
131  public boolean hasNarrative() {
132    if (!canHaveNarrative()) {
133      return false;
134    }
135    ResourceWrapper text = child("text");
136    if (text == null) {
137      return false;
138    }
139    ResourceWrapper div = text.child("div");
140    if (div == null) {
141      return false;
142    }
143    return ((ResourceWrapperNative) div).element.getXhtml() != null;
144  }
145
146  public void setNarrative(XhtmlNode x, String status, boolean multiLangMode, Locale locale, boolean isPretty) {
147    if (element instanceof DomainResource) {
148      DomainResource r = (DomainResource) element;    
149      r.getText().setUserData(UserDataNames.renderer_is_generated, true);
150      if (!r.hasText() || !r.getText().hasDiv()) {
151        r.setText(new Narrative());
152        r.getText().setStatusAsString(status);      
153      }
154      if (multiLangMode) {
155        if (!r.getText().hasDiv()) { 
156          XhtmlNode div = new XhtmlNode(NodeType.Element, "div");
157          div.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
158          r.getText().setDiv(div);
159        } else {
160          r.getText().getDiv().getChildNodes().removeIf(c -> !"div".equals(c.getName()) || !c.hasAttribute("xml:lang"));
161        }
162        markLanguage(x, locale);
163        r.getText().getDiv().addChildNode(x);
164      } else {
165        if (!x.hasAttribute("xmlns"))
166          x.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
167        if (r.hasLanguage()) {
168          // use both - see https://www.w3.org/TR/i18n-html-tech-lang/#langvalues
169          x.setAttribute("lang", r.getLanguage());
170          x.setAttribute("xml:lang", r.getLanguage());
171        }
172        r.getText().setDiv(x);
173      }
174    } else {
175      throw new Error("Cannot call setNarrative on a "+element.fhirType());
176    }
177  }
178
179  public void markLanguage(XhtmlNode x, Locale locale) {
180    x.setAttribute("lang", locale.toLanguageTag());
181    x.setAttribute("xml:lang", locale.toLanguageTag());
182    x.addTag(0, "hr");
183    x.addTag(0, "p").b().tx(locale.getDisplayName());
184    x.addTag(0, "hr");
185  }
186
187
188  public String getId() {
189    return element.getIdBase();
190  }
191
192  @Override
193  public String toString() {
194    return name + (index == -1 ? "" : "["+index+"]")+": "+fhirType()+" ("+kind+"/"+path()+"): native = "+element.fhirType()+" -> "+element.toString();      
195  }
196
197  public Resource getResourceNative() {
198    ResourceWrapper focus = getResourceWrapper();
199    return focus == null ? null : (Resource) ((ResourceWrapperNative) focus).element;
200  }
201
202  public boolean hasFormatComment() {
203    return element.hasFormatComment();
204  }
205
206  public Collection<String> getFormatCommentsPre() {
207    return element.getFormatCommentsPre();
208  }
209
210  public XhtmlNode getXhtml() {
211    return element.getXhtml();
212  }
213
214  public Base getBase() {
215    return element;
216  }
217
218  public boolean isDirect() {
219    return true;
220  }
221
222  public String getWebPath() {
223    if (isResource()) {
224      return ((Resource) element).getWebPath();
225    } else {
226      return null;
227    }
228  }
229
230  public String getCodeSystemUri() {
231    if (element instanceof Enumeration<?>) {
232      return ((Enumeration<?>) element).getSystem();
233    }
234    return null;
235  }
236
237  @Override
238  public boolean hasUserData(String name) {
239    return element.hasUserData(name);
240  }
241
242  @Override
243  public Object getUserData(String name) {
244    return element.getUserData(name);
245  }
246
247}