001package org.hl7.fhir.r5.renderers.utils;
002
003import java.io.IOException;
004import java.util.ArrayList;
005import java.util.Collection;
006import java.util.List;
007import java.util.Locale;
008
009import org.hl7.fhir.r5.context.ContextUtilities;
010import org.hl7.fhir.r5.elementmodel.Element;
011import org.hl7.fhir.r5.model.Base;
012import org.hl7.fhir.r5.model.DataType;
013import org.hl7.fhir.r5.model.Resource;
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 abstract class ResourceWrapper {
021
022  public enum ElementKind {
023    PrimitiveType,
024    DataType,
025    BackboneElement,
026    ContainedResource,
027    InlineResource,
028    BundleEntry,
029    IndependentResource
030  }
031  
032  public static class NamedResourceWrapperList {
033    private String name;
034    private String url; // for extension definitions
035    private List<ResourceWrapper> values = new ArrayList<ResourceWrapper>();
036
037    public NamedResourceWrapperList(String name) {
038      super();
039      this.name = name;
040    }
041
042    public NamedResourceWrapperList(String name, String url) {
043      super();
044      this.name = name;
045      this.url = url;
046    }
047    
048    public String getName() {
049      return name;
050    }
051    
052    public String getUrl() {
053      return url;
054    }
055
056    public List<ResourceWrapper> getValues() {
057      return values;
058    }
059//    public ElementDefinition getPropertyDefinition() {
060//      return values.isEmpty() ? null : values.get(0).getPropertyDefinition();
061//    }
062//    public StructureDefinition getClassDefinition() {
063//      return values.isEmpty() ? null : values.get(0).getClassDefinition();
064//    }
065  }
066
067  protected ContextUtilities contextUtils;
068  protected ResourceWrapper parent;
069  protected String name; // null at root
070  protected int index; // -1 if not repeating
071  protected ElementKind kind;
072
073  protected List<ResourceWrapper> children;
074
075  // -- Constructors ------------------------------------------------------------------
076  
077  protected ResourceWrapper() {
078    // TODO Auto-generated constructor stub
079  }
080
081  public static ResourceWrapper forResource(ContextUtilities contextUtils, Resource resource) {
082    ResourceWrapperNative self = new ResourceWrapperNative();
083    self.contextUtils = contextUtils;
084    self.parent = null;
085    self.name = null;
086    self.index = -1;
087    self.kind = ElementKind.IndependentResource;
088    self.element = resource;
089    return self;
090  }
091
092  public static ResourceWrapper forResource(ContextUtilities contextUtils, Element resource) {
093    ResourceWrapperModel self = new ResourceWrapperModel();
094    self.contextUtils = contextUtils;
095    self.parent = null;
096    self.name = null;
097    self.index = -1;
098    self.kind = ElementKind.IndependentResource;
099    self.model = resource;
100    return self;
101  }
102
103  public static ResourceWrapper forResource(RenderingContext context, Resource resource) {
104    return forResource(context.getContextUtilities(), resource);
105  }
106  
107  public static ResourceWrapper forResource(RenderingContext context, Element resource) {
108    return forResource(context.getContextUtilities(), resource);
109  }
110  
111  public static ResourceWrapper forType(ContextUtilities contextUtils, Element resource) {
112    ResourceWrapperModel self = new ResourceWrapperModel();
113    self.contextUtils = contextUtils;
114    self.parent = null;
115    self.name = null;
116    self.index = -1;
117    self.kind = ElementKind.DataType;
118    self.model = resource;
119    return self;
120  }
121
122  public static ResourceWrapper forType(ContextUtilities contextUtils, DataType type) {
123    ResourceWrapperNative self = new ResourceWrapperNative();
124    self.contextUtils = contextUtils;
125    self.parent = null;
126    self.name = null;
127    self.index = -1;
128    self.kind = null;
129    self.element = type;
130    return self;
131  }
132  
133  public static ResourceWrapper forType(ContextUtilities contextUtils, ResourceWrapper parent, DataType type) {
134    ResourceWrapperNative self = new ResourceWrapperNative();
135    self.contextUtils = contextUtils;
136    self.parent = parent;
137    self.name = null;
138    self.index = -1;
139    self.kind = null;
140    self.element = type;
141    return self;
142  }
143
144  
145  public String path() {
146    if (parent == null) {
147      return fhirType();
148    } else {
149      return parent.path()+"." + (index == -1 ? name : name+"["+index+"]");
150    }
151  }
152
153  protected String basePath() {
154    if (parent == null || this.isResource()) {
155      return this.fhirType();
156    } else {
157      return parent.basePath()+"."+name;
158    }
159  }
160
161  public ElementKind kind() {
162    return kind;
163  }
164
165  public String name() {
166    return name;
167  }
168
169  public int index() {
170    return index;
171  }
172
173  public boolean isPrimitive(String name) {
174    ResourceWrapper child = child(name);
175    return child != null && child.isPrimitive();
176  }
177
178  public boolean hasPrimitiveValue(String name) {
179    ResourceWrapper child = child(name);
180    return child != null && child.hasPrimitiveValue();
181  }
182
183  public String primitiveValue(String name) {
184    ResourceWrapper child = child(name);
185    return child == null ? null : child.primitiveValue();
186  }
187
188  public String primitiveValueMN(String... names) {
189    ResourceWrapper child = childMN(names);
190    return child == null ? null : child.primitiveValue();
191  }
192
193  public String firstPrimitiveValue(String name) {
194    ResourceWrapper child = firstChild(name);
195    return child == null ? null : child.primitiveValue();
196  }
197
198  private void loadChildren() {
199    if (children == null) {
200      children = new ArrayList<>();
201      loadTheChildren();
202    }
203  }
204
205
206  public List<ResourceWrapper> children() {
207    loadChildren();
208    return children;
209  }
210
211  public List<NamedResourceWrapperList> childrenInGroups() {
212    loadChildren();
213    List<NamedResourceWrapperList> list = new ArrayList<ResourceWrapper.NamedResourceWrapperList>(); 
214    for (ResourceWrapper e : children) {
215      NamedResourceWrapperList nl = null;
216      for (NamedResourceWrapperList t : list) {
217        if (t.name.equals(e.name())) {
218          nl = t;
219        }
220      }
221      if (nl == null) {
222        nl = new NamedResourceWrapperList(e.name());
223        list.add(nl);
224      }
225      nl.values.add(e);
226    }
227    return list;
228  }
229
230  public List<ResourceWrapper> children(String name) {
231    loadChildren();
232    List<ResourceWrapper> list = new ArrayList<ResourceWrapper>();
233    for (ResourceWrapper e : children) {
234      if (name.equals(e.name())) {
235        list.add(e);
236      }
237    }
238    return list;
239  }
240
241  /**
242   * For when an item has been renamed - find by any of the names
243   * @param name
244   * @return
245   */
246  public List<ResourceWrapper> childrenMN(String... names) {
247    loadChildren();
248    List<ResourceWrapper> list = new ArrayList<ResourceWrapper>();
249    for (ResourceWrapper e : children) {
250      for (String name : names) {
251        if (name.equals(e.name())) {
252          list.add(e);
253        }
254      }
255    }
256    return list;
257  }
258
259  public ResourceWrapper child(String name) {
260    loadChildren();
261    
262    ResourceWrapper res = null;
263
264    for (ResourceWrapper e : children) {
265      if (name.equals(e.name()) || (name+"[x]").equals(e.name())) {
266        if (res == null) {
267          res = e;
268        } else {
269          throw new Error("Duplicated element '"+name+"' @ '"+path()+"'");
270        }
271      }
272    }
273    return res;
274  }
275
276  /** 
277   * For when an item has been renamed - find by any of the names
278   * @param names
279   * @return
280   */
281  public ResourceWrapper childMN(String... names) {
282    loadChildren();
283
284    ResourceWrapper res = null;
285
286    for (ResourceWrapper e : children) {
287      for (String name : names) {
288        if (name.equals(e.name()) || (name+"[x]").equals(e.name())) {
289          if (res == null) {
290            res = e;
291          } else {
292            throw new Error("Duplicated element '"+name+"' @ '"+path()+"'");
293          }
294        }
295      }
296    }
297    return res;
298  }
299
300  public boolean has(String name) {
301    loadChildren();
302    for (ResourceWrapper e : children) {
303      if (name.equals(e.name()) || (name+"[x]").equals(e.name())) {
304        return true;
305      }
306    }
307    return false;
308  }
309
310  public boolean hasMN(String... names) {
311    loadChildren();
312    for (ResourceWrapper e : children) {
313      for (String name : names) {
314        if (name.equals(e.name()) || (name+"[x]").equals(e.name())) {
315          return true;
316        }
317      }
318    }
319    return false;
320  }
321
322  public ResourceWrapper resource() {
323    ResourceWrapper e = this.parent;
324    while (e != null && !e.isResource()) {
325      e = e.parent;
326    }
327    return e;
328  }
329
330  public boolean hasChildren() {
331    loadChildren();
332    return !children.isEmpty();
333  }
334
335  public boolean hasExtension(String url) {
336    loadChildren();
337    for (ResourceWrapper e : children) {
338      if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) {
339        return true;
340      }
341    }
342    return false;
343  }
344  
345  public ResourceWrapper extension(String url) {
346    ResourceWrapper res = null;
347    loadChildren();
348    for (ResourceWrapper e : children) {
349      if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) {
350        if (res == null) {
351          res = e;
352        } else {
353          throw new Error("Duplicated extension '"+url+"' @ '"+path()+"'");
354        }
355      }
356    }
357    return res;
358  }
359    
360  public ResourceWrapper extensionValue(String url) {
361    ResourceWrapper res = null;
362    loadChildren();
363    for (ResourceWrapper e : children) {
364      if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) {
365        if (res == null) {
366          res = e.child("value");
367        } else {
368          throw new Error("Duplicated extension '"+url+"' @ '"+path()+"'");
369        }
370      }
371    }
372    return res;
373  }
374  
375  public List<ResourceWrapper> extensions(String url) {
376    List<ResourceWrapper> res = new ArrayList<ResourceWrapper>();
377    loadChildren();
378    for (ResourceWrapper e : children) {
379      if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) {
380        res.add(e);
381      }
382    }
383    return res;
384  }
385  
386  public List<ResourceWrapper> extensions() {
387    List<ResourceWrapper> res = new ArrayList<ResourceWrapper>();
388    loadChildren();
389    for (ResourceWrapper e : children) {
390      if ("Extension".equals(e.fhirType())) {
391        res.add(e);
392      }
393    }
394    return res;
395  }
396  
397  public List<ResourceWrapper> extensionValues(String url) {
398    List<ResourceWrapper> res = new ArrayList<ResourceWrapper>();
399    loadChildren();
400    for (ResourceWrapper e : children) {
401      if ("Extension".equals(e.fhirType()) && url.equals(e.primitiveValue("url"))) {
402        if (e.has("value")) {
403          res.add(e.child("value"));
404        }
405      }
406    }
407    return res;
408  }
409
410  public abstract Resource getResourceNative();  
411  public abstract boolean canHaveNarrative();
412  public abstract XhtmlNode getNarrative();  
413  public abstract boolean hasNarrative();
414  public abstract void setNarrative(XhtmlNode x, String status, boolean multiLangMode, Locale locale, boolean isPretty) throws IOException;
415  public abstract String getId();
416
417  public void markLanguage(XhtmlNode x, Locale locale) {
418    x.setAttribute("lang", locale.toString());
419    x.setAttribute("xml:lang", locale.toString());
420    x.addTag(0, "hr");
421    x.addTag(0, "p").b().tx(locale.getDisplayName());
422    x.addTag(0, "hr");
423  }
424  
425
426  public boolean matches(ResourceWrapper b) {
427    if (isEmpty() || b.isEmpty()) {
428      return isEmpty() && b.isEmpty();
429    } else {
430      if (hasPrimitiveValue() || b.hasPrimitiveValue()) {
431        if (!hasPrimitiveValue() || !b.hasPrimitiveValue() || !primitiveValue().equals(b.primitiveValue())) {
432          return false;
433        }
434      }
435      if (children().size() != b.children().size()) {
436        return false;
437      } else {
438        for (int i = 0; i < children().size(); i++) {
439          if (!children().get(i).matches(b.children().get(i))) {
440            return false;
441          }
442        }
443        return true;
444      }
445    }
446  }
447
448  public String extensionString(String url) {
449    ResourceWrapper re = extensionValue(url);
450    return re == null ?  null : re.primitiveValue();
451  }
452
453  public boolean isEmpty() {
454    if (hasChildren()) {
455      for (ResourceWrapper c : children) {
456        if (!c.isEmpty()) {
457          return false;
458        }
459      }
460    }
461    return !isPrimitive() || !hasPrimitiveValue();
462  }
463
464  
465  public ResourceWrapper getResourceWrapper() {
466    ResourceWrapper focus = this;
467    while (focus != null && !focus.isResource()) {
468      focus = focus.parent;
469    }
470    return focus;
471  }
472
473  public ResourceWrapper firstChild(String name) {
474    List<ResourceWrapper> list = children(name);
475    return list.size() == 0 ? null : list.get(0);
476  }
477
478  public ContextUtilities getContextUtilities() {
479    return contextUtils;
480  }
481
482  public String getScopedId() {
483    if (!isResource()) {
484      return null;
485    } else {
486      String res = getId();
487      if (parent != null) {
488        res = parent.getResourceWrapper().getScopedId()+"/"+getId();
489      }
490      return res;
491    }
492  }
493
494  public ResourceWrapper parent() {
495    return parent;
496  }
497
498  public ResourceWrapper getContained(String id) {
499    if (isResource()) {
500      List<ResourceWrapper> contained = children("contained");
501      for (ResourceWrapper e : contained) {
502        if (id.equals(e.getId())) {
503          return e;
504        }
505      }
506    }
507    return null;
508  }
509
510
511  public abstract String getCodeSystemUri();
512  public abstract boolean hasFormatComment();
513  public abstract Collection<String> getFormatCommentsPre();
514  public abstract XhtmlNode getXhtml();
515  public abstract Base getBase();
516  public abstract String getWebPath();
517  public abstract boolean isDirect();
518  protected abstract void loadTheChildren();
519  public abstract String fhirVersion();
520  public abstract String fhirType();
521  public abstract boolean isPrimitive();
522  public abstract boolean hasPrimitiveValue();
523  public abstract String primitiveValue();
524  public abstract boolean isResource();
525
526
527}